Revision
This commit is contained in:
parent
1b02eb1bce
commit
c623016841
|
|
@ -0,0 +1,62 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const events = new Map();
|
||||||
|
|
||||||
|
function on(event, callback) {
|
||||||
|
if (!events.has(event)) {
|
||||||
|
events.set(event, []);
|
||||||
|
}
|
||||||
|
events.get(event).push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function emit(event, ...args) {
|
||||||
|
if (events.has(event)) {
|
||||||
|
for (const callback of events.get(event)) {
|
||||||
|
try {
|
||||||
|
callback(...args);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Event error [${event}]:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function once(event, callback) {
|
||||||
|
const wrapper = (...args) => {
|
||||||
|
off(event, wrapper);
|
||||||
|
callback(...args);
|
||||||
|
};
|
||||||
|
on(event, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
function off(event, callback) {
|
||||||
|
if (events.has(event)) {
|
||||||
|
const callbacks = events.get(event);
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index > -1) {
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAllListeners(event) {
|
||||||
|
if (event) {
|
||||||
|
events.delete(event);
|
||||||
|
} else {
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listenerCount(event) {
|
||||||
|
return events.has(event) ? events.get(event).length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
on,
|
||||||
|
emit,
|
||||||
|
once,
|
||||||
|
off,
|
||||||
|
removeAllListeners,
|
||||||
|
listenerCount,
|
||||||
|
events
|
||||||
|
};
|
||||||
|
|
@ -4,48 +4,40 @@ let http = require("http");
|
||||||
let express = require("express");
|
let express = require("express");
|
||||||
let compression = require("compression");
|
let compression = require("compression");
|
||||||
let {resolve} = require("path");
|
let {resolve} = require("path");
|
||||||
let auth = require("express-basic-auth");
|
|
||||||
|
|
||||||
const { termoutput } = require("./config");
|
const { termoutput } = require("./config");
|
||||||
let server = http.createServer();
|
|
||||||
let app = express();
|
|
||||||
server.addListener("request", app);
|
|
||||||
app.use(compression({
|
|
||||||
level: 9
|
|
||||||
}));
|
|
||||||
|
|
||||||
server.listen(7707,'0.0.0.0',() => {
|
let app = express();
|
||||||
|
app.use(compression({ level: 9 }));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
let server = http.createServer(app);
|
||||||
|
|
||||||
|
server.listen(7707, '0.0.0.0', () => {
|
||||||
termoutput && console.log("HTTP Service Running...");
|
termoutput && console.log("HTTP Service Running...");
|
||||||
});
|
});
|
||||||
server.addListener("error",(err)=> {
|
|
||||||
console.err(err)
|
server.on("error", (err) => {
|
||||||
})
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
exports.http = server;
|
exports.http = server;
|
||||||
|
|
||||||
app.get("/script",(request, response)=>{
|
const apiRouter = require("./api");
|
||||||
response.sendFile(resolve("./script/index.js"))
|
app.use("/api", apiRouter);
|
||||||
});
|
|
||||||
app.get("/test",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./script/test.html"))
|
|
||||||
});
|
|
||||||
app.get("/index.js.map",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./script/index.js.map"))
|
|
||||||
});
|
|
||||||
app.get("/stream",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./public/index.html"))
|
|
||||||
});
|
|
||||||
app.get("/",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./script/index.html"))
|
|
||||||
});
|
|
||||||
app.get("/console",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./console/index.html"))
|
|
||||||
});
|
|
||||||
app.get("/console/lib.js",(request, response)=>{
|
|
||||||
response.sendFile(resolve("./console/lib.js"))
|
|
||||||
});
|
|
||||||
app.use("/stream",express.static(resolve("./public")));
|
|
||||||
app.use("/console/",express.static(resolve("./console")));
|
|
||||||
|
|
||||||
app.get("*",(request, response)=>{
|
app.get("/script", (req, res) => {
|
||||||
response.sendFile(resolve("./script/status.xml"))
|
res.sendFile(resolve("./script/index.js"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.use(express.static(resolve("./public")));
|
||||||
|
app.use("/script", express.static(resolve("./script")));
|
||||||
|
|
||||||
|
app.get("/", (req, res) => {
|
||||||
|
res.sendFile(resolve("./script/index.js"));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("*", (req, res) => {
|
||||||
|
res.sendFile(resolve("./script/status.xml"));
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const handlers = new Map();
|
||||||
|
|
||||||
|
function register(type, handler) {
|
||||||
|
handlers.set(type, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle(client, message) {
|
||||||
|
const { type } = message;
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
return { status: 'fail', message: 'MISSING_TYPE' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = handlers.get(type);
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return { status: 'fail', message: 'UNKNOWN_TYPE' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = handler(client, message);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Handler error [${type}]:`, error);
|
||||||
|
return { status: 'fail', message: 'HANDLER_ERROR', error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregister(type) {
|
||||||
|
handlers.delete(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
handlers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasHandler(type) {
|
||||||
|
return handlers.has(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function listHandlers() {
|
||||||
|
return [...handlers.keys()];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
register,
|
||||||
|
handle,
|
||||||
|
unregister,
|
||||||
|
clear,
|
||||||
|
hasHandler,
|
||||||
|
listHandlers
|
||||||
|
};
|
||||||
|
|
@ -1,232 +1,175 @@
|
||||||
const { Client } = require("../Client.js");
|
"use strict";
|
||||||
let {addService, addListener} = require("../WebSocket.js");
|
|
||||||
|
|
||||||
addListener('disconnect',(global, xclient)=>{
|
const { Client } = require("../Client.js");
|
||||||
const {intersection, pairs} = xclient.getSucureClients();
|
const { on, emit, register } = require("../WebSocket");
|
||||||
for (const [clientid, client] of intersection)
|
|
||||||
{
|
on('disconnect', (xclient) => {
|
||||||
client?.send([
|
const { intersection, pairs } = xclient.getSucureClients();
|
||||||
{
|
|
||||||
id: xclient.id
|
for (const [clientid, client] of intersection) {
|
||||||
},
|
client?.send([{ id: xclient.id }, "peer/disconnect"]);
|
||||||
"peer/disconnect"
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [id, peer] of pairs)
|
for (const [id, peer] of pairs) {
|
||||||
{
|
|
||||||
peer?.pairs.delete(xclient.id);
|
peer?.pairs.delete(xclient.id);
|
||||||
xclient.pairs.delete(id);
|
xclient.pairs.delete(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addService(({
|
register('auth/pair-system', (client, msg) => {
|
||||||
client,
|
if (msg.value == 'everybody') {
|
||||||
message,
|
client.requiredPair = true;
|
||||||
end,
|
return { status: 'success' };
|
||||||
next
|
|
||||||
})=>{
|
|
||||||
let {type,to,value,name} = message;
|
|
||||||
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case "auth/pair-system":{
|
|
||||||
if(value == 'everybody')
|
|
||||||
{
|
|
||||||
client.requiredPair = true;
|
|
||||||
end({status:"success"});
|
|
||||||
client.sync('requiredPair');
|
|
||||||
}
|
|
||||||
if(value == 'disable')
|
|
||||||
{
|
|
||||||
client.requiredPair = false;
|
|
||||||
end({status:"success"});
|
|
||||||
client.sync('requiredPair');
|
|
||||||
//CLIENT_UPDATE_PROP(client.id, 'requiredPair', client.requiredPair);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "my/socketid":{
|
|
||||||
end(client.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'auth/public':{
|
|
||||||
client.requiredPair = false;
|
|
||||||
client.sync('requiredPair');
|
|
||||||
return end({
|
|
||||||
value: 'success',
|
|
||||||
mode: 'public'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
case 'auth/private':{
|
|
||||||
client.requiredPair = true;
|
|
||||||
client.sync('requiredPair');
|
|
||||||
return end({
|
|
||||||
value: 'success',
|
|
||||||
mode: 'private'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
case 'request/pair':{
|
|
||||||
if(Client.clients.has(to)){
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'CLIENT-NOT-FOUND'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let pairclient = Client.clients.get(to);
|
|
||||||
if(pairclient.pairs.has(client.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'success',
|
|
||||||
message: 'ALREADY-PAIRED'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if(client.pairs.has(to))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'ALREADY-REQUESTED'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
end({
|
|
||||||
status: 'success',
|
|
||||||
message: 'REQUESTED'
|
|
||||||
})
|
|
||||||
client.peerRequest(pairclient);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'accept/pair':{
|
|
||||||
if(Client.clients.has(to)){
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'CLIENT-NOT-FOUND'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let pairclient = Client.clients.get(to);
|
|
||||||
if(pairclient.pairs.has(client.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'success',
|
|
||||||
message: 'ALREADY-PAIRED'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if(!client.pairs.has(to))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'NOT-REQUESTED-PAIR'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
client.acceptPeerRequest(pairclient);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'reject/pair':{
|
|
||||||
if(Client.clients.has(to)){
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'CLIENT-NOT-FOUND'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let pairclient = Client.clients.get(to);
|
|
||||||
if(pairclient.pairs.has(client.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'success',
|
|
||||||
message: 'ALREADY-PAIRED'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if(!client.pairs.has(to))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'NOT-REQUESTED-PAIR'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
client.rejectPeerRequest(pairclient);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'end/pair':{
|
|
||||||
if(Client.clients.has(to)){
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
message: 'CLIENT-NOT-FOUND'
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let pairclient = Client.clients.get(to);
|
|
||||||
if(!pairclient.pairs.has(client.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'success',
|
|
||||||
message: 'NOT-PAIRED'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
client.rejectPeerRequest(pairclient);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'pair/list':{
|
|
||||||
end({
|
|
||||||
type:'pair/list',
|
|
||||||
value: client.pairList()
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'is/reachable':{
|
|
||||||
if(Client.clients.has(to))
|
|
||||||
{
|
|
||||||
let otherPeer = Client.clients.get(to);
|
|
||||||
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
|
|
||||||
{
|
|
||||||
end(false);
|
|
||||||
}else{
|
|
||||||
end(true);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
end(false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'auth/info':{
|
|
||||||
client.info.set(name, value);
|
|
||||||
let clients = client.getSucureClients();
|
|
||||||
for (const [,spair] of clients.pairs)
|
|
||||||
{
|
|
||||||
spair.send([{
|
|
||||||
from: client.id,
|
|
||||||
name,
|
|
||||||
value
|
|
||||||
},"pair/info"]);
|
|
||||||
};
|
|
||||||
for (const [,spair] of clients.roompairs)
|
|
||||||
{
|
|
||||||
spair.send([{
|
|
||||||
from: client.id,
|
|
||||||
name,
|
|
||||||
value
|
|
||||||
},"pair/info"]);
|
|
||||||
};
|
|
||||||
return end({
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case 'peer/info':{
|
|
||||||
if(client.isSecure(message.peer))
|
|
||||||
{
|
|
||||||
let info = {};
|
|
||||||
let peer = Client.clients.get(message.peer);
|
|
||||||
peer.info.forEach((value, name) => info[name] = value);
|
|
||||||
return end({
|
|
||||||
status: "success",
|
|
||||||
info
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
return end({
|
|
||||||
status: "fail",
|
|
||||||
message: "unaccessible user"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
if (msg.value == 'disable') {
|
||||||
|
client.requiredPair = false;
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
return { status: 'fail', message: 'INVALID_VALUE' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('my/socketid', (client, msg) => {
|
||||||
|
return client.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
register('auth/public', (client, msg) => {
|
||||||
|
client.requiredPair = false;
|
||||||
|
return { value: 'success', mode: 'public' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('auth/private', (client, msg) => {
|
||||||
|
client.requiredPair = true;
|
||||||
|
return { value: 'success', mode: 'private' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('request/pair', (client, msg) => {
|
||||||
|
const { to } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairclient = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (pairclient.pairs.has(client.id)) {
|
||||||
|
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.pairs.has(to)) {
|
||||||
|
return { status: 'fail', message: 'ALREADY-REQUESTED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
client.peerRequest(pairclient);
|
||||||
|
return { status: 'success', message: 'REQUESTED' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('accept/pair', (client, msg) => {
|
||||||
|
const { to } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairclient = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (pairclient.pairs.has(client.id)) {
|
||||||
|
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.pairs.has(to)) {
|
||||||
|
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
|
||||||
|
}
|
||||||
|
|
||||||
|
client.acceptPeerRequest(pairclient);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('reject/pair', (client, msg) => {
|
||||||
|
const { to } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairclient = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (pairclient.pairs.has(client.id)) {
|
||||||
|
return { status: 'success', message: 'ALREADY-PAIRED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.pairs.has(to)) {
|
||||||
|
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
|
||||||
|
}
|
||||||
|
|
||||||
|
client.rejectPeerRequest(pairclient);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('end/pair', (client, msg) => {
|
||||||
|
const { to } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pairclient = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (!pairclient.pairs.has(client.id)) {
|
||||||
|
return { status: 'success', message: 'NOT_PAIRED' };
|
||||||
|
}
|
||||||
|
|
||||||
|
client.rejectPeerRequest(pairclient);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('pair/list', (client, msg) => {
|
||||||
|
return { type: 'pair/list', value: client.pairList() };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('is/reachable', (client, msg) => {
|
||||||
|
const { to } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherPeer = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
register('auth/info', (client, msg) => {
|
||||||
|
const { name, value } = msg;
|
||||||
|
|
||||||
|
client.info.set(name, value);
|
||||||
|
|
||||||
|
const clients = client.getSucureClients();
|
||||||
|
|
||||||
|
for (const [, spair] of clients.pairs) {
|
||||||
|
spair.send([{ from: client.id, name, value }, "pair/info"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [, spair] of clients.roompairs) {
|
||||||
|
spair.send([{ from: client.id, name, value }, "pair/info"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('peer/info', (client, msg) => {
|
||||||
|
const { peer } = msg;
|
||||||
|
|
||||||
|
if (!client.isSecure(peer)) {
|
||||||
|
return { status: "fail", message: "unaccessible user" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const peerClient = Client.clients.get(peer);
|
||||||
|
const info = {};
|
||||||
|
peerClient.info.forEach((value, name) => { info[name] = value; });
|
||||||
|
|
||||||
|
return { status: "success", info };
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,168 +1,101 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
const { Client } = require("../Client.js");
|
const { Client } = require("../Client.js");
|
||||||
let {randomUUID} = require("crypto");
|
const { register } = require("../WebSocket");
|
||||||
let {addService,addListener} = require("../WebSocket.js");
|
const { Room } = require("./Room");
|
||||||
const { Room } = require("./Room.js");
|
|
||||||
|
|
||||||
/*
|
register('pack/to', (client, msg) => {
|
||||||
Peer to peer veri aktarımı
|
const { to, pack, handshake } = msg;
|
||||||
|
|
||||||
- Kişiden kişiye direkt veri aktarımı
|
if (!client.packReadable()) {
|
||||||
- Kişiler arası tünelleme / Kişiler arası özel protokol
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
- Oda katılımcıları içerisinde veri aktarımı
|
}
|
||||||
- Oda katılımcıları arasında belli kişilere veri aktarımı
|
|
||||||
|
|
||||||
*/
|
if (!Client.clients.has(to)) {
|
||||||
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
addService(({
|
const otherPeer = Client.clients.get(to);
|
||||||
client,
|
|
||||||
end,
|
if (otherPeer.requiredPair) {
|
||||||
global,
|
if (!otherPeer.pairs.has(to)) {
|
||||||
message,
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
next,
|
|
||||||
response,
|
|
||||||
messageId
|
|
||||||
}) => {
|
|
||||||
let {type} = message;
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case "pack/to":{
|
|
||||||
let {to,pack,handshake} = message;
|
|
||||||
|
|
||||||
if(!client.packReadable()){
|
|
||||||
console.error("Okunabilir olmayan client bir mesaj iletiyor")
|
|
||||||
handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Client.clients.has(to))
|
|
||||||
{
|
|
||||||
let otherPeer = Client.clients.get(to);
|
|
||||||
if(otherPeer.requiredPair)
|
|
||||||
{
|
|
||||||
if(!otherPeer.pairs.has(to))
|
|
||||||
{
|
|
||||||
console.error("Client, güvenilir olmayan bir cliente mesaj iletiyor")
|
|
||||||
return handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(!otherPeer.pairs.has(to))
|
|
||||||
{
|
|
||||||
otherPeer.pairs.add(client.id);
|
|
||||||
client.pairs.add(otherPeer.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!otherPeer.packWriteable()){
|
|
||||||
console.error("Client, yazılabilir olmayan bir cliente mesaj iletiyor")
|
|
||||||
handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
otherPeer.send([{
|
|
||||||
from: client.id,
|
|
||||||
pack: pack
|
|
||||||
}, 'pack']);
|
|
||||||
handshake && end({
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
console.error("Client, olmayan bir cliente mesaj iletiyor")
|
|
||||||
handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "request/to":{
|
} else {
|
||||||
let {to,pack} = message;
|
if (!otherPeer.pairs.has(to)) {
|
||||||
if(Client.clients.has(to))
|
otherPeer.pairs.add(client.id);
|
||||||
{
|
client.pairs.add(otherPeer.id);
|
||||||
let otherPeer = Client.clients.get(to);
|
|
||||||
if(otherPeer.requiredPair)
|
|
||||||
{
|
|
||||||
if(!otherPeer.pairs.has(to))
|
|
||||||
{
|
|
||||||
console.error("Client, güvenilir olmayan bir clientden veri istiyor")
|
|
||||||
return handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
otherPeer.pairs.add(client.id);
|
|
||||||
client.pairs.add(otherPeer.id);
|
|
||||||
}
|
|
||||||
otherPeer.send([{
|
|
||||||
from: client.id,
|
|
||||||
pack: pack,
|
|
||||||
id:messageId
|
|
||||||
}, 'request']);
|
|
||||||
}else console.error("request/to error not finded Peer");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "response/to":{
|
}
|
||||||
let {to,pack, id} = message;
|
|
||||||
if(Client.clients.has(to))
|
|
||||||
{
|
|
||||||
let otherPeer = Client.clients.get(to);
|
|
||||||
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
|
|
||||||
{
|
|
||||||
console.error("response istenen peer güvenli değil")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
otherPeer.send([{
|
|
||||||
from: client.id,
|
|
||||||
pack: pack
|
|
||||||
}, id]);
|
|
||||||
}else console.error("response/to error not finded Peer");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "pack/room":{
|
|
||||||
let {to,pack, handshake,wom} = message;
|
|
||||||
|
|
||||||
|
if (!otherPeer.packWriteable()) {
|
||||||
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if(!client.packReadable()){
|
otherPeer.send([{ from: client.id, pack }, 'pack']);
|
||||||
console.error("Client paketi okumak için müsait değil")
|
|
||||||
handshake && end({
|
return handshake ? { type: 'success' } : undefined;
|
||||||
type: 'fail'
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Room.rooms.has(to))
|
register('request/to', (client, msg) => {
|
||||||
{
|
const { to, pack } = msg;
|
||||||
if(!client.rooms.has(to))
|
|
||||||
{
|
if (!Client.clients.has(to)) {
|
||||||
console.error("Client katılmadığı bir odaya mesaj iletiyor")
|
return;
|
||||||
return handshake && end({
|
}
|
||||||
type: 'fail'
|
|
||||||
})
|
const otherPeer = Client.clients.get(to);
|
||||||
};
|
|
||||||
Room.rooms.get(to).send(
|
if (otherPeer.requiredPair) {
|
||||||
[
|
if (!otherPeer.pairs.has(to)) {
|
||||||
{
|
return;
|
||||||
from: to,
|
|
||||||
pack: pack,
|
|
||||||
sender: client.id
|
|
||||||
}, 'pack/room'
|
|
||||||
],
|
|
||||||
wom ? client.id : void 0,
|
|
||||||
client => client.packWriteable()
|
|
||||||
);
|
|
||||||
handshake && end({
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
console.error("Olmayan oda için veri gönderme isteniyor")
|
|
||||||
handshake && end({
|
|
||||||
type: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:{
|
} else {
|
||||||
next();
|
otherPeer.pairs.add(client.id);
|
||||||
}
|
client.pairs.add(otherPeer.id);
|
||||||
};
|
}
|
||||||
});
|
|
||||||
|
otherPeer.send([{ from: client.id, pack }, 'request']);
|
||||||
|
});
|
||||||
|
|
||||||
|
register('response/to', (client, msg) => {
|
||||||
|
const { to, pack, id } = msg;
|
||||||
|
|
||||||
|
if (!Client.clients.has(to)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherPeer = Client.clients.get(to);
|
||||||
|
|
||||||
|
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
otherPeer.send([{ from: client.id, pack }, id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
register('pack/room', (client, msg) => {
|
||||||
|
const { to, pack, handshake, wom } = msg;
|
||||||
|
|
||||||
|
if (!client.packReadable()) {
|
||||||
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Room.rooms.has(to)) {
|
||||||
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.rooms.has(to)) {
|
||||||
|
return handshake ? { type: 'fail' } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(to);
|
||||||
|
|
||||||
|
room.send(
|
||||||
|
[{ from: to, pack, sender: client.id }, 'pack/room'],
|
||||||
|
wom ? client.id : undefined,
|
||||||
|
c => c.packWriteable()
|
||||||
|
);
|
||||||
|
|
||||||
|
return handshake ? { type: 'success' } : undefined;
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,394 +1,269 @@
|
||||||
const { Client } = require("../Client");
|
"use strict";
|
||||||
let {addService, addListener} = require("../WebSocket.js");
|
|
||||||
|
|
||||||
class APNumber{
|
const { Client } = require("../Client");
|
||||||
/**
|
const { on, register } = require("../WebSocket");
|
||||||
* @type {Map<Number, Client>}
|
|
||||||
*/
|
class APNumber {
|
||||||
static busyNumbers = new Map();
|
static busyNumbers = new Map();
|
||||||
/**
|
|
||||||
* @type {number}
|
static lock(client) {
|
||||||
* @param {Client} client
|
|
||||||
*/
|
|
||||||
static lock(client)
|
|
||||||
{
|
|
||||||
let c = 24;
|
let c = 24;
|
||||||
while(true){
|
while (true) {
|
||||||
if(!APNumber.busyNumbers.has(c))
|
if (!APNumber.busyNumbers.has(c)) {
|
||||||
{
|
APNumber.busyNumbers.set(c, client);
|
||||||
APNumber.busyNumbers.set(c,client);
|
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_NUMBER/LOCK',
|
type: 'AP_NUMBER/LOCK',
|
||||||
uuid: client.id,
|
uuid: client.id,
|
||||||
value: c
|
value: c
|
||||||
})
|
});
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @param {number} num
|
static release(num) {
|
||||||
*/
|
|
||||||
static release(num)
|
|
||||||
{
|
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_NUMBER/RELEASE',
|
type: 'AP_NUMBER/RELEASE',
|
||||||
uuid: APNumber.busyNumbers.get(num).id,
|
uuid: APNumber.busyNumbers.get(num).id,
|
||||||
value: num
|
value: num
|
||||||
})
|
});
|
||||||
APNumber.busyNumbers.delete(num);
|
APNumber.busyNumbers.delete(num);
|
||||||
}
|
}
|
||||||
static whois(num){
|
|
||||||
|
static whois(num) {
|
||||||
return APNumber.busyNumbers.get(num)?.id;
|
return APNumber.busyNumbers.get(num)?.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class APShortCode{
|
class APShortCode {
|
||||||
/**
|
|
||||||
* @type {Map<string, Client>}
|
|
||||||
*/
|
|
||||||
static busyCodes = new Map();
|
static busyCodes = new Map();
|
||||||
/**
|
|
||||||
*
|
static lock(client) {
|
||||||
* @param {Client} client
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static lock(client){
|
|
||||||
let firstLetter = new ShortCodeLetter();
|
let firstLetter = new ShortCodeLetter();
|
||||||
let secondLetter = new ShortCodeLetter();
|
let secondLetter = new ShortCodeLetter();
|
||||||
let thirdLetter = new ShortCodeLetter();
|
let thirdLetter = new ShortCodeLetter();
|
||||||
while(1)
|
|
||||||
{
|
while (1) {
|
||||||
let code = [firstLetter.code,secondLetter.code,thirdLetter.code].join('');
|
let code = [firstLetter.code, secondLetter.code, thirdLetter.code].join('');
|
||||||
if(APShortCode.busyCodes.has(code) == false)
|
if (!APShortCode.busyCodes.has(code)) {
|
||||||
{
|
|
||||||
APShortCode.busyCodes.set(code, client);
|
APShortCode.busyCodes.set(code, client);
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_SHORTCODE/LOCK',
|
type: 'AP_SHORTCODE/LOCK',
|
||||||
uuid: APShortCode.busyCodes.get(code).id,
|
uuid: APShortCode.busyCodes.get(code).id,
|
||||||
value: code
|
value: code
|
||||||
})
|
});
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
if(!thirdLetter.end())
|
|
||||||
{
|
if (!thirdLetter.end()) {
|
||||||
thirdLetter.next()
|
thirdLetter.next();
|
||||||
}else{
|
} else {
|
||||||
thirdLetter.reset()
|
thirdLetter.reset();
|
||||||
if(!secondLetter.end())
|
if (!secondLetter.end()) {
|
||||||
{
|
|
||||||
secondLetter.next();
|
secondLetter.next();
|
||||||
}else{
|
} else {
|
||||||
secondLetter.reset();
|
secondLetter.reset();
|
||||||
if(!firstLetter.end())
|
if (!firstLetter.end()) {
|
||||||
{
|
|
||||||
firstLetter.next();
|
firstLetter.next();
|
||||||
}else{
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static release(code){
|
|
||||||
if(APShortCode.busyCodes.has(code))
|
static release(code) {
|
||||||
{
|
if (APShortCode.busyCodes.has(code)) {
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_SHORTCODE/RELEASE',
|
type: 'AP_SHORTCODE/RELEASE',
|
||||||
uuid: APShortCode.busyCodes.get(code).id,
|
uuid: APShortCode.busyCodes.get(code).id,
|
||||||
value: code
|
value: code
|
||||||
})
|
});
|
||||||
APShortCode.busyCodes.delete(code);
|
APShortCode.busyCodes.delete(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static whois(num){
|
|
||||||
|
static whois(num) {
|
||||||
return APShortCode.busyCodes.get(num)?.id;
|
return APShortCode.busyCodes.get(num)?.id;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
class ShortCodeLetter{
|
|
||||||
chars = 'ABCDEFGHIKLMNOPRSTVXYZ'.split('');
|
|
||||||
now = 0;
|
|
||||||
code = 'A';
|
|
||||||
next(){
|
|
||||||
this.now++
|
|
||||||
this.code = this.chars.at(this.now);
|
|
||||||
return this.code;
|
|
||||||
}
|
|
||||||
reset(){
|
|
||||||
this.now = 0;
|
|
||||||
this.code = 'A';
|
|
||||||
}
|
|
||||||
end(){
|
|
||||||
return !this.chars.at(this.now + 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class APIPAddress{
|
class ShortCodeLetter {
|
||||||
/**
|
chars = 'ABCDEFGHIKLMNOPRSTVXYZ'.split('');
|
||||||
* @type {Map<[number,number,number,number], Client>}
|
now = 0;
|
||||||
*/
|
code = 'A';
|
||||||
|
|
||||||
|
next() {
|
||||||
|
this.now++;
|
||||||
|
this.code = this.chars.at(this.now);
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.now = 0;
|
||||||
|
this.code = 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
return !this.chars.at(this.now + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIPAddress {
|
||||||
static busyIP = new Map();
|
static busyIP = new Map();
|
||||||
/**
|
|
||||||
* @param {Client} client
|
static lock(client) {
|
||||||
*/
|
let A = 10, B = 0, C = 0, D = 1;
|
||||||
static lock(client){
|
|
||||||
let A = 10;
|
while (1) {
|
||||||
let B = 0;
|
let code = [A, B, C, D].join('.');
|
||||||
let C = 0;
|
if (!APIPAddress.busyIP.has(code)) {
|
||||||
let D = 1;
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
let code = [A,B,C,D].join('.');
|
|
||||||
if(APIPAddress.busyIP.has(code) == false)
|
|
||||||
{
|
|
||||||
APIPAddress.busyIP.set(code, client);
|
APIPAddress.busyIP.set(code, client);
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_IPADDRESS/LOCK',
|
type: 'AP_IPADDRESS/LOCK',
|
||||||
uuid: APIPAddress.busyIP.get(code).id,
|
uuid: APIPAddress.busyIP.get(code).id,
|
||||||
value: code
|
value: code
|
||||||
})
|
});
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
if(D != 255)
|
|
||||||
{
|
if (D != 255) { D++; continue; }
|
||||||
D++;
|
D = 0;
|
||||||
continue;
|
if (C != 255) { C++; continue; }
|
||||||
}else{
|
C = 0;
|
||||||
D = 0;
|
if (B != 255) { B++; continue; }
|
||||||
}
|
B = 0;
|
||||||
if(C != 255)
|
if (A != 255) { A++; continue; }
|
||||||
{
|
|
||||||
C++;
|
|
||||||
continue;
|
|
||||||
}else{
|
|
||||||
C = 0;
|
|
||||||
}
|
|
||||||
if(B != 255)
|
|
||||||
{
|
|
||||||
B++;
|
|
||||||
continue;
|
|
||||||
}else{
|
|
||||||
B = 0;
|
|
||||||
}
|
|
||||||
if(A != 255)
|
|
||||||
{
|
|
||||||
A++;
|
|
||||||
continue;
|
|
||||||
}else{
|
|
||||||
A = 0;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static release(code){
|
|
||||||
if(APIPAddress.busyIP.has(code))
|
static release(code) {
|
||||||
{
|
if (APIPAddress.busyIP.has(code)) {
|
||||||
process.send({
|
process.send({
|
||||||
type: 'AP_IPADDRESS/RELEASE',
|
type: 'AP_IPADDRESS/RELEASE',
|
||||||
uuid: APIPAddress.busyIP.get(code).id,
|
uuid: APIPAddress.busyIP.get(code).id,
|
||||||
value: code
|
value: code
|
||||||
})
|
});
|
||||||
APIPAddress.busyIP.delete(code);
|
APIPAddress.busyIP.delete(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static whois(num){
|
|
||||||
|
static whois(num) {
|
||||||
return APIPAddress.busyIP.get(num)?.id;
|
return APIPAddress.busyIP.get(num)?.id;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.APNumber = APNumber;
|
exports.APNumber = APNumber;
|
||||||
exports.APShortCode = APShortCode;
|
exports.APShortCode = APShortCode;
|
||||||
exports.APIPAddress = APIPAddress;
|
exports.APIPAddress = APIPAddress;
|
||||||
|
|
||||||
addService(({
|
register('alloc/APIPAddress', (client, msg) => {
|
||||||
client,
|
if (client.APIPAddress) {
|
||||||
message,
|
return { status: 'success', ip: client.APIPAddress };
|
||||||
end,
|
|
||||||
next
|
|
||||||
})=>{
|
|
||||||
let {type,whois} = message;
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case "alloc/APIPAddress":{
|
|
||||||
if(client.APIPAddress) {
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
ip : client.APIPAddress
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let value = APIPAddress.lock(client);
|
|
||||||
client.APIPAddress = value;
|
|
||||||
client.sync('APIPAddress');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
ip : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "alloc/APNumber":{
|
|
||||||
if(client.APNumber) {
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
number : client.APNumber
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let value = APNumber.lock(client);
|
|
||||||
client.APNumber = value;
|
|
||||||
client.sync('APNumber');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
number : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "alloc/APShortCode":{
|
|
||||||
if(client.APShortCode) {
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
code : client.APShortCode
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let value = APShortCode.lock(client);
|
|
||||||
client.APShortCode = value;
|
|
||||||
client.sync('APShortCode');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
code : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "realloc/APIPAddress":{
|
|
||||||
if(client.APIPAddress == 0){
|
|
||||||
return end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
APIPAddress.release(client.APIPAddress);
|
|
||||||
let value = APIPAddress.lock(client);
|
|
||||||
client.sync('APIPAddress');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
ip : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "realloc/APNumber":{
|
|
||||||
if(client.APNumber == 0){
|
|
||||||
return end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
APNumber.release(client.APNumber);
|
|
||||||
let value = APNumber.lock(client);
|
|
||||||
client.sync('APNumber');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
number : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "realloc/APShortCode":{
|
|
||||||
if(client.APShortCode == 0){
|
|
||||||
return end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
APShortCode.release(client.APShortCode);
|
|
||||||
let value = APShortCode.lock(client);
|
|
||||||
client.sync('APShortCode');
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
code : value
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "release/APIPAddress":{
|
|
||||||
APIPAddress.release(client.APIPAddress);
|
|
||||||
client.APIPAddress = void 0;
|
|
||||||
client.sync('APShortCode');
|
|
||||||
end({
|
|
||||||
status : "success"
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "release/APNumber":{
|
|
||||||
APNumber.release(client.APNumber);
|
|
||||||
client.APNumber = void 0;
|
|
||||||
client.sync('APIPAddress');
|
|
||||||
end({
|
|
||||||
status : "success"
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "release/APShortCode":{
|
|
||||||
APShortCode.release(client.APShortCode);
|
|
||||||
client.APShortCode = void 0;
|
|
||||||
client.sync('APIPAddress');
|
|
||||||
end({
|
|
||||||
status : "success"
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "whois/APIPAddress":{
|
|
||||||
let socketId = APIPAddress.whois(whois);
|
|
||||||
if(socketId)
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
socket : socketId
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "whois/APNumber":{
|
|
||||||
let socketId = APNumber.whois(whois);
|
|
||||||
if(socketId)
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
socket : socketId
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "whois/APShortCode":{
|
|
||||||
let socketId = APShortCode.whois(whois);
|
|
||||||
if(socketId)
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "success",
|
|
||||||
socket : socketId
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status : "fail"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
let value = APIPAddress.lock(client);
|
||||||
|
client.APIPAddress = value;
|
||||||
|
return { status: 'success', ip: value };
|
||||||
|
});
|
||||||
|
|
||||||
addListener('disconnect',(global, client)=>{
|
register('alloc/APNumber', (client, msg) => {
|
||||||
if(client.APIPAddress != 0)
|
if (client.APNumber) {
|
||||||
{
|
return { status: 'success', number: client.APNumber };
|
||||||
|
}
|
||||||
|
let value = APNumber.lock(client);
|
||||||
|
client.APNumber = value;
|
||||||
|
return { status: 'success', number: value };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('alloc/APShortCode', (client, msg) => {
|
||||||
|
if (client.APShortCode) {
|
||||||
|
return { status: 'success', code: client.APShortCode };
|
||||||
|
}
|
||||||
|
let value = APShortCode.lock(client);
|
||||||
|
client.APShortCode = value;
|
||||||
|
return { status: 'success', code: value };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('realloc/APIPAddress', (client, msg) => {
|
||||||
|
if (client.APIPAddress == 0) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
APIPAddress.release(client.APIPAddress);
|
||||||
|
let value = APIPAddress.lock(client);
|
||||||
|
return { status: 'success', ip: value };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('realloc/APNumber', (client, msg) => {
|
||||||
|
if (client.APNumber == 0) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
APNumber.release(client.APNumber);
|
||||||
|
let value = APNumber.lock(client);
|
||||||
|
return { status: 'success', number: value };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('realloc/APShortCode', (client, msg) => {
|
||||||
|
if (client.APShortCode == 0) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
APShortCode.release(client.APShortCode);
|
||||||
|
let value = APShortCode.lock(client);
|
||||||
|
return { status: 'success', code: value };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('release/APIPAddress', (client, msg) => {
|
||||||
|
APIPAddress.release(client.APIPAddress);
|
||||||
|
client.APIPAddress = undefined;
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('release/APNumber', (client, msg) => {
|
||||||
|
APNumber.release(client.APNumber);
|
||||||
|
client.APNumber = undefined;
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('release/APShortCode', (client, msg) => {
|
||||||
|
APShortCode.release(client.APShortCode);
|
||||||
|
client.APShortCode = undefined;
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('whois/APIPAddress', (client, msg) => {
|
||||||
|
let socketId = APIPAddress.whois(msg.whois);
|
||||||
|
if (socketId) {
|
||||||
|
return { status: 'success', socket: socketId };
|
||||||
|
}
|
||||||
|
return { status: 'fail' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('whois/APNumber', (client, msg) => {
|
||||||
|
let socketId = APNumber.whois(msg.whois);
|
||||||
|
if (socketId) {
|
||||||
|
return { status: 'success', socket: socketId };
|
||||||
|
}
|
||||||
|
return { status: 'fail' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('whois/APShortCode', (client, msg) => {
|
||||||
|
let socketId = APShortCode.whois(msg.whois);
|
||||||
|
if (socketId) {
|
||||||
|
return { status: 'success', socket: socketId };
|
||||||
|
}
|
||||||
|
return { status: 'fail' };
|
||||||
|
});
|
||||||
|
|
||||||
|
on('disconnect', (client) => {
|
||||||
|
if (client.APIPAddress != 0) {
|
||||||
APIPAddress.release(client.APIPAddress);
|
APIPAddress.release(client.APIPAddress);
|
||||||
}
|
}
|
||||||
if(client.APNumber != 0)
|
if (client.APNumber != 0) {
|
||||||
{
|
|
||||||
APNumber.release(client.APNumber);
|
APNumber.release(client.APNumber);
|
||||||
}
|
}
|
||||||
if(client.APShortCode != 0)
|
if (client.APShortCode != 0) {
|
||||||
{
|
|
||||||
APShortCode.release(client.APShortCode);
|
APShortCode.release(client.APShortCode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
const { Client } = require("../Client.js");
|
const { Client } = require("../Client.js");
|
||||||
let {randomUUID,createHash} = require("crypto");
|
let { randomUUID, createHash } = require("crypto");
|
||||||
const joi = require("joi");
|
const joi = require("joi");
|
||||||
let {addService,addListener} = require("../WebSocket.js");
|
const { on, register } = require("../WebSocket");
|
||||||
const { termoutput } = require("../config.js");
|
const { termoutput } = require("../config.js");
|
||||||
let term = require("terminal-kit").terminal;
|
let term = require("terminal-kit").terminal;
|
||||||
|
|
||||||
function Sha256(update)
|
function Sha256(update) {
|
||||||
{
|
|
||||||
return createHash("sha256").update(update).digest("hex");
|
return createHash("sha256").update(update).digest("hex");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -233,7 +232,7 @@ Room.prototype.eject = function(client){
|
||||||
*/
|
*/
|
||||||
Room.rooms = new Map();
|
Room.rooms = new Map();
|
||||||
|
|
||||||
addListener('connect',(global, client)=>{
|
on('connect', (client) => {
|
||||||
let room = new Room();
|
let room = new Room();
|
||||||
room.accessType = "private";
|
room.accessType = "private";
|
||||||
room.joinType = "notify";
|
room.joinType = "notify";
|
||||||
|
|
@ -245,10 +244,12 @@ addListener('connect',(global, client)=>{
|
||||||
room.join(client);
|
room.join(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
addListener('disconnect',(global, client)=>{
|
on('disconnect', (client) => {
|
||||||
Room.rooms.get(client.id).eject(client);
|
const room = Room.rooms.get(client.id);
|
||||||
|
if (room) room.eject(client);
|
||||||
for (const roomId of client.rooms) {
|
for (const roomId of client.rooms) {
|
||||||
Room.rooms.get(roomId).eject(client);
|
const r = Room.rooms.get(roomId);
|
||||||
|
if (r) r.eject(client);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -267,466 +268,282 @@ let CreateRoomVerify = joi.object({
|
||||||
autoFetchInfo: joi.boolean().optional(),
|
autoFetchInfo: joi.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
addService(({
|
register('myroom-info', (client, msg) => {
|
||||||
client,
|
let room = Room.rooms.get(client.id);
|
||||||
end,
|
return { status: "success", room: room.toJSON() };
|
||||||
global,
|
|
||||||
message,
|
|
||||||
next,
|
|
||||||
response
|
|
||||||
})=>{
|
|
||||||
let {type} = message;
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case 'myroom-info':{
|
|
||||||
let room = Room.rooms.get(client.id);
|
|
||||||
end({
|
|
||||||
status: "success",
|
|
||||||
room: room.toJSON()
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'room-peers':{
|
|
||||||
let {roomId,filter} = message;
|
|
||||||
if(Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
let filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
|
||||||
end({
|
|
||||||
status: 'success',
|
|
||||||
peers: filteredPeers.map(i => i.id)
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'room/peer-count':{
|
|
||||||
let {roomId,filter} = message;
|
|
||||||
if(Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
let filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
|
||||||
end({
|
|
||||||
status: 'success',
|
|
||||||
count: filteredPeers.length
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'room-info':{
|
|
||||||
let {name} = message;
|
|
||||||
for (const [roomId,{name:RoomName}] of Room.rooms) {
|
|
||||||
if(name == RoomName)
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "success",
|
|
||||||
room : Room.rooms.get(roomId).toJSON()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'joinedrooms':{
|
|
||||||
let data = [
|
|
||||||
...client.rooms
|
|
||||||
].map(e => {
|
|
||||||
return Room.rooms.get(e).toJSON()
|
|
||||||
});
|
|
||||||
end(data)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'closeroom':{
|
|
||||||
let {roomId} = message;
|
|
||||||
if(Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
if(room.owner === client.id)
|
|
||||||
{
|
|
||||||
room.down();
|
|
||||||
end({
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status: 'fail'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
end({
|
|
||||||
status: 'fail'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'create-room':{
|
|
||||||
let {error} = CreateRoomVerify.validate(message);
|
|
||||||
if(error)
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: 'fail',
|
|
||||||
messages: error.message
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
let {name} = message;
|
|
||||||
for (const [,{name:RoomName}] of Room.rooms) {
|
|
||||||
if(name == RoomName)
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "ALREADY-EXISTS"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let room = new Room();
|
|
||||||
room.accessType = message.accessType;
|
|
||||||
room.notifyActionInvite = message.notifyActionInvite;
|
|
||||||
room.notifyActionJoined = message.notifyActionJoined;
|
|
||||||
room.notifyActionEjected = message.notifyActionEjected;
|
|
||||||
room.joinType = message.joinType;
|
|
||||||
room.description = message.description;
|
|
||||||
room.name = message.name;
|
|
||||||
room.owner = client;
|
|
||||||
if(message.credential)
|
|
||||||
{
|
|
||||||
room.credential = Sha256(message.credential + "");
|
|
||||||
}
|
|
||||||
room.publish();
|
|
||||||
room.join(client);
|
|
||||||
end({
|
|
||||||
status: "success",
|
|
||||||
room: room.toJSON()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'joinroom':{
|
|
||||||
let {name,autoFetchInfo} = message;
|
|
||||||
let roomId;
|
|
||||||
for (const [_roomId,{name:RoomName}] of Room.rooms) {
|
|
||||||
if(name == RoomName)
|
|
||||||
{
|
|
||||||
roomId = _roomId
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let isRoom = Room.rooms.has(roomId);
|
|
||||||
if(isRoom)
|
|
||||||
{
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
if(room.joinType == "lock")
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "LOCKED-ROOM"
|
|
||||||
})
|
|
||||||
}else if(room.joinType == "password")
|
|
||||||
{
|
|
||||||
if(room.credential == Sha256(message.credential + ""))
|
|
||||||
{
|
|
||||||
let info = {};
|
|
||||||
if(autoFetchInfo)
|
|
||||||
{
|
|
||||||
info.info = room.getInfo();
|
|
||||||
};
|
|
||||||
room.join(client);
|
|
||||||
return end({
|
|
||||||
status : "success",
|
|
||||||
room: room.toJSON()
|
|
||||||
})
|
|
||||||
}else return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "WRONG-PASSWORD",
|
|
||||||
area: "credential"
|
|
||||||
})
|
|
||||||
}else if(room.joinType == "free"){
|
|
||||||
let info = {};
|
|
||||||
if(autoFetchInfo)
|
|
||||||
{
|
|
||||||
info.info = room.getInfo();
|
|
||||||
};
|
|
||||||
room.join(client);
|
|
||||||
return end({
|
|
||||||
status : "success",
|
|
||||||
room: room.toJSON(),
|
|
||||||
...info
|
|
||||||
})
|
|
||||||
}else if(room.joinType == "invite"){
|
|
||||||
room.waitingInvited.add(client.id);
|
|
||||||
if(room.notifyActionInvite)
|
|
||||||
{
|
|
||||||
room.send([{
|
|
||||||
id: client.id
|
|
||||||
},"room/invite"]);
|
|
||||||
}else{
|
|
||||||
room.owner.send([{
|
|
||||||
id: client.id
|
|
||||||
},"room/invite"]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}else{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ejectroom':{
|
|
||||||
let {roomId} = message;
|
|
||||||
let isRoom = Room.rooms.has(roomId);
|
|
||||||
if(isRoom)
|
|
||||||
{
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
if(room.clients.has(client.id))
|
|
||||||
{
|
|
||||||
room.eject(client)
|
|
||||||
return end({
|
|
||||||
status : "success"
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "ALREADY-ROOM-OUT"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'accept/invite-room':{
|
|
||||||
let {roomId, clientId} = message;
|
|
||||||
// Odanın varlığının kontrolü
|
|
||||||
if(!Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
|
|
||||||
// erişim kontrolü
|
|
||||||
if(!client.rooms.has(room.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "FORBIDDEN-INVITE-ACTIONS"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Odaya katılma şeklinin doğruluğu
|
|
||||||
if(room.joinType == 'invite')
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "INVALID-DATA"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// Odaya katılma talebinin doğruluğu
|
|
||||||
if(!room.waitingInvited.includes(clientId))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-WAITING-INVITED"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// Odaya katılan kişinin varlığı
|
|
||||||
if(!Client.clients.has(clientId))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-CLIENT"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Odaya kişiyi kabul etme
|
|
||||||
let JoinClient = Client.clients.get(clientId)
|
|
||||||
|
|
||||||
room.join(JoinClient);
|
|
||||||
|
|
||||||
JoinClient.send([{
|
|
||||||
status:"accepted"
|
|
||||||
},'room/invite/status']);
|
|
||||||
|
|
||||||
return end({
|
|
||||||
status : "success"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case 'reject/invite-room':{
|
|
||||||
let {roomId, clientId} = message;
|
|
||||||
// Odanın varlığının kontrolü
|
|
||||||
if(!Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
|
|
||||||
// erişim kontrolü
|
|
||||||
if(!client.rooms.has(room.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "FORBIDDEN-INVITE-ACTIONS"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Odaya katılma şeklinin doğruluğu
|
|
||||||
if(room.joinType == 'invite')
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "INVALID-DATA"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// Odaya katılma talebinin doğruluğu
|
|
||||||
if(!room.waitingInvited.includes(clientId))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-WAITING-INVITED"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// Odaya katılan kişinin varlığı
|
|
||||||
if(!Client.clients.has(clientId))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-CLIENT"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Odaya davet edilen kişiyi reddetme
|
|
||||||
let JoinClient = Client.clients.get(clientId)
|
|
||||||
|
|
||||||
room.waitingInvited = room.waitingInvited.filter(e => e != clientId);
|
|
||||||
|
|
||||||
room.send([{id:clientId,roomId:room.id},'room/invite/status'])
|
|
||||||
|
|
||||||
JoinClient.send([{
|
|
||||||
status:"rejected"
|
|
||||||
},'room/invite/status']);
|
|
||||||
|
|
||||||
return end({
|
|
||||||
status : "success"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case 'room/list':{
|
|
||||||
let rooms = [];
|
|
||||||
for (const [id, {accessType,name,joinType,description}] of Room.rooms)
|
|
||||||
{
|
|
||||||
if(accessType == "public")
|
|
||||||
{
|
|
||||||
rooms.push({
|
|
||||||
name,
|
|
||||||
joinType,
|
|
||||||
description,
|
|
||||||
id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end({
|
|
||||||
type:'public/rooms',
|
|
||||||
rooms
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case 'room/info':{
|
|
||||||
let {roomId,name} = message;
|
|
||||||
|
|
||||||
// Odanın varlığının kontrolü
|
|
||||||
if(!Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
|
|
||||||
// erişim kontrolü
|
|
||||||
if(!client.rooms.has(room.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-jıoned-ROOM"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if(name)
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status: "success",
|
|
||||||
value: room.info.get(name)
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
return end({
|
|
||||||
status: "success",
|
|
||||||
value: room.getInfo()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'room/setinfo':{
|
|
||||||
let {roomId,name,value} = message;
|
|
||||||
|
|
||||||
// Odanın varlığının kontrolü
|
|
||||||
if(!Room.rooms.has(roomId))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NOT-FOUND-ROOM"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let room = Room.rooms.get(roomId);
|
|
||||||
|
|
||||||
// erişim kontrolü
|
|
||||||
if(!client.rooms.has(room.id))
|
|
||||||
{
|
|
||||||
return end({
|
|
||||||
status : "fail",
|
|
||||||
message : "NO-JOINED-ROOM"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
room.info.set(name, value);
|
|
||||||
|
|
||||||
room.send(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
roomId:room.id
|
|
||||||
},
|
|
||||||
"room/info"
|
|
||||||
],
|
|
||||||
client.id,
|
|
||||||
client => {
|
|
||||||
return client.roomInfoNotifiable()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return end({
|
|
||||||
status: "success"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('room-peers', (client, msg) => {
|
||||||
|
const { roomId, filter } = msg;
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||||
|
return { status: 'success', peers: filteredPeers.map(i => i.id) };
|
||||||
|
});
|
||||||
|
|
||||||
exports.Room = Room;
|
register('room/peer-count', (client, msg) => {
|
||||||
|
const { roomId, filter } = msg;
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
|
||||||
|
return { status: 'success', count: filteredPeers.length };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('room-info', (client, msg) => {
|
||||||
|
const { name } = msg;
|
||||||
|
for (const [roomId, room] of Room.rooms) {
|
||||||
|
if (name == room.name) {
|
||||||
|
return { status: "success", room: room.toJSON() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('joinedrooms', (client, msg) => {
|
||||||
|
return [...client.rooms].map(e => Room.rooms.get(e).toJSON());
|
||||||
|
});
|
||||||
|
|
||||||
|
register('closeroom', (client, msg) => {
|
||||||
|
const { roomId } = msg;
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: 'fail' };
|
||||||
|
}
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
if (room.owner === client.id) {
|
||||||
|
room.down();
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
return { status: 'fail' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('create-room', (client, msg) => {
|
||||||
|
const { error } = CreateRoomValidate.validate(msg);
|
||||||
|
if (error) {
|
||||||
|
return { status: 'fail', messages: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name } = msg;
|
||||||
|
for (const [, room] of Room.rooms) {
|
||||||
|
if (name == room.name) {
|
||||||
|
return { status: "fail", message: "ALREADY-EXISTS" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let room = new Room();
|
||||||
|
room.accessType = msg.accessType;
|
||||||
|
room.notifyActionInvite = msg.notifyActionInvite;
|
||||||
|
room.notifyActionJoined = msg.notifyActionJoined;
|
||||||
|
room.notifyActionEjected = msg.notifyActionEjected;
|
||||||
|
room.joinType = msg.joinType;
|
||||||
|
room.description = msg.description;
|
||||||
|
room.name = msg.name;
|
||||||
|
room.owner = client;
|
||||||
|
|
||||||
|
if (msg.credential) {
|
||||||
|
room.credential = Sha256(msg.credential + "");
|
||||||
|
}
|
||||||
|
|
||||||
|
room.publish();
|
||||||
|
room.join(client);
|
||||||
|
|
||||||
|
return { status: "success", room: room.toJSON() };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('joinroom', (client, msg) => {
|
||||||
|
const { name, autoFetchInfo } = msg;
|
||||||
|
let roomId;
|
||||||
|
|
||||||
|
for (const [_roomId, room] of Room.rooms) {
|
||||||
|
if (name == room.name) {
|
||||||
|
roomId = _roomId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (room.joinType == "lock") {
|
||||||
|
return { status: "fail", message: "LOCKED-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType == "password") {
|
||||||
|
if (room.credential == Sha256(msg.credential + "")) {
|
||||||
|
let info = {};
|
||||||
|
if (autoFetchInfo) {
|
||||||
|
info.info = room.getInfo();
|
||||||
|
}
|
||||||
|
room.join(client);
|
||||||
|
return { status: "success", room: room.toJSON(), ...info };
|
||||||
|
}
|
||||||
|
return { status: "fail", message: "WRONG-PASSWORD", area: "credential" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType == "free") {
|
||||||
|
let info = {};
|
||||||
|
if (autoFetchInfo) {
|
||||||
|
info.info = room.getInfo();
|
||||||
|
}
|
||||||
|
room.join(client);
|
||||||
|
return { status: "success", room: room.toJSON(), ...info };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType == "invite") {
|
||||||
|
room.waitingInvited.add(client.id);
|
||||||
|
if (room.notifyActionInvite) {
|
||||||
|
room.send([{ id: client.id }, "room/invite"]);
|
||||||
|
} else {
|
||||||
|
room.owner.send([{ id: client.id }, "room/invite"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('ejectroom', (client, msg) => {
|
||||||
|
const { roomId } = msg;
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (!room.clients.has(client.id)) {
|
||||||
|
return { status: "fail", message: "ALREADY-ROOM-OUT" };
|
||||||
|
}
|
||||||
|
|
||||||
|
room.eject(client);
|
||||||
|
return { status: "success" };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('accept/invite-room', (client, msg) => {
|
||||||
|
const { roomId, clientId } = msg;
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (!client.rooms.has(room.id)) {
|
||||||
|
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType == 'invite') {
|
||||||
|
return { status: "fail", message: "INVALID-DATA" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!room.waitingInvited.includes(clientId)) {
|
||||||
|
return { status: "fail", message: "NO-WAITING-INVITED" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Client.clients.has(clientId)) {
|
||||||
|
return { status: "fail", message: "NO-CLIENT" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const JoinClient = Client.clients.get(clientId);
|
||||||
|
room.join(JoinClient);
|
||||||
|
JoinClient.send([{ status: "accepted" }, 'room/invite/status']);
|
||||||
|
|
||||||
|
return { status: "success" };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('reject/invite-room', (client, msg) => {
|
||||||
|
const { roomId, clientId } = msg;
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (!client.rooms.has(room.id)) {
|
||||||
|
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType == 'invite') {
|
||||||
|
return { status: "fail", message: "INVALID-DATA" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!room.waitingInvited.includes(clientId)) {
|
||||||
|
return { status: "fail", message: "NO-WAITING-INVITED" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Client.clients.has(clientId)) {
|
||||||
|
return { status: "fail", message: "NO-CLIENT" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const JoinClient = Client.clients.get(clientId);
|
||||||
|
room.waitingInvited = room.waitingInvited.filter(e => e != clientId);
|
||||||
|
room.send([{ id: clientId, roomId: room.id }, 'room/invite/status']);
|
||||||
|
JoinClient.send([{ status: "rejected" }, 'room/invite/status']);
|
||||||
|
|
||||||
|
return { status: "success" };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('room/list', (client, msg) => {
|
||||||
|
const rooms = [];
|
||||||
|
for (const [id, room] of Room.rooms) {
|
||||||
|
if (room.accessType == "public") {
|
||||||
|
rooms.push({
|
||||||
|
name: room.name,
|
||||||
|
joinType: room.joinType,
|
||||||
|
description: room.description,
|
||||||
|
id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { type: 'public/rooms', rooms };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('room/info', (client, msg) => {
|
||||||
|
const { roomId, name } = msg;
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (!client.rooms.has(room.id)) {
|
||||||
|
return { status: "fail", message: "NO-JOINED-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
return { status: "success", value: room.info.get(name) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: "success", value: room.getInfo() };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('room/setinfo', (client, msg) => {
|
||||||
|
const { roomId, name, value } = msg;
|
||||||
|
|
||||||
|
if (!Room.rooms.has(roomId)) {
|
||||||
|
return { status: "fail", message: "NOT-FOUND-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = Room.rooms.get(roomId);
|
||||||
|
|
||||||
|
if (!client.rooms.has(room.id)) {
|
||||||
|
return { status: "fail", message: "NO-JOINED-ROOM" };
|
||||||
|
}
|
||||||
|
|
||||||
|
room.info.set(name, value);
|
||||||
|
|
||||||
|
room.send(
|
||||||
|
[{ name, value, roomId: room.id }, "room/info"],
|
||||||
|
client.id,
|
||||||
|
c => c.roomInfoNotifiable()
|
||||||
|
);
|
||||||
|
|
||||||
|
return { status: "success" };
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.Room = Room;
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,43 @@
|
||||||
let {addService,addListener} = require("../WebSocket.js");
|
"use strict";
|
||||||
|
|
||||||
const Stdoutput = [
|
const { on, emit, register } = require("../WebSocket");
|
||||||
["notifyPairInfo", true],
|
|
||||||
["packrecaive", true],
|
|
||||||
["packsending", true],
|
|
||||||
["notifyRoomInfo", true]
|
|
||||||
];
|
|
||||||
|
|
||||||
addListener('connect',(global, client)=>{
|
const defaults = {
|
||||||
for (const [name, defaultValue] of Stdoutput)
|
notifyPairInfo: true,
|
||||||
{
|
packrecaive: true,
|
||||||
client.store.set(name, defaultValue);
|
packsending: true,
|
||||||
|
notifyRoomInfo: true
|
||||||
|
};
|
||||||
|
|
||||||
|
on('connect', (client) => {
|
||||||
|
for (const [name, value] of Object.entries(defaults)) {
|
||||||
|
client.store.set(name, value);
|
||||||
}
|
}
|
||||||
client.sync('store');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('connection/pairinfo', (client, msg) => {
|
||||||
|
client.store.set("notifyPairInfo", !!msg.value);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
|
register('connection/roominfo', (client, msg) => {
|
||||||
|
client.store.set("notifyRoomInfo", !!msg.value);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
addService(({
|
register('connection/packrecaive', (client, msg) => {
|
||||||
client,
|
client.store.set("packrecaive", !!msg.value);
|
||||||
end,
|
return { status: 'success' };
|
||||||
global,
|
});
|
||||||
message,
|
|
||||||
next,
|
|
||||||
response
|
|
||||||
})=>{
|
|
||||||
let {type, value} = message;
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
// enable/disable pair/info messages
|
|
||||||
case 'connection/pairinfo':{
|
|
||||||
client.store.set("notifyPairInfo", !!value)
|
|
||||||
client.sync('store');
|
|
||||||
end(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'connection/roominfo':{
|
|
||||||
client.store.set("notifyRoomInfo", !!value)
|
|
||||||
client.sync('store');
|
|
||||||
end(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
register('connection/packsending', (client, msg) => {
|
||||||
|
client.store.set("packsending", !!msg.value);
|
||||||
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
||||||
case 'connection/packrecaive':{
|
register('connection/reset', (client, msg) => {
|
||||||
client.store.set("packrecaive", !!value)
|
for (const [name, value] of Object.entries(defaults)) {
|
||||||
client.sync('store');
|
client.store.set(name, value);
|
||||||
end(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'connection/packsending':{
|
|
||||||
client.store.set("packsending", !!value)
|
|
||||||
client.sync('store');
|
|
||||||
end(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'connection/reset':{
|
|
||||||
for (const [name, defaultValue] of Stdoutput)
|
|
||||||
{
|
|
||||||
client.store.set(name, defaultValue);
|
|
||||||
}
|
|
||||||
client.sync('store');
|
|
||||||
end(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
return { status: 'success' };
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
let {addListener} = require("../WebSocket.js");
|
"use strict";
|
||||||
|
|
||||||
addListener('connect',(global, client)=>{
|
const { on } = require("../WebSocket");
|
||||||
client.send([{
|
|
||||||
type: 'id',
|
on('connect', (client) => {
|
||||||
value: client.id
|
client.send([{ type: 'id', value: client.id }, 'id']);
|
||||||
},'id'])
|
});
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,147 +1,94 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let websocket = require("websocket");
|
let websocket = require("websocket");
|
||||||
let {http} = require("./HTTPServer");
|
let http = null;
|
||||||
|
let wsServer = null;
|
||||||
let {randomUUID} = require("crypto");
|
let {randomUUID} = require("crypto");
|
||||||
const { Client } = require("./Client.js");
|
const { Client } = require("./Client.js");
|
||||||
const { termoutput } = require("./config");
|
const { termoutput } = require("./config");
|
||||||
termoutput && console.log("Web Socket Protocol is ready");
|
const EventEmitter = require("./EventEmitter");
|
||||||
|
const MessageRouter = require("./MessageRouter");
|
||||||
|
|
||||||
http.addListener("upgrade",() => {
|
function init(server) {
|
||||||
termoutput && console.log("HTTP Upgrading to WebSocket");
|
http = server;
|
||||||
})
|
|
||||||
|
|
||||||
let wsServer = new websocket.server({
|
|
||||||
httpServer: http,
|
|
||||||
autoAcceptConnections: true
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
process.send({
|
|
||||||
core: "writestat",
|
|
||||||
writeBytes:0,
|
|
||||||
readBytes:0,
|
|
||||||
totalBytes:0,
|
|
||||||
recaivedPacket:0,
|
|
||||||
sendedPacket:0,
|
|
||||||
totalPacket:0
|
|
||||||
})*/
|
|
||||||
|
|
||||||
let global = new Map();
|
|
||||||
let clients = new Map();
|
|
||||||
|
|
||||||
|
|
||||||
wsServer.addListener("connect",(socket) => {
|
|
||||||
let xClient = new Client();
|
|
||||||
let id = randomUUID();
|
|
||||||
socket.id = id;
|
|
||||||
xClient.id = id;
|
|
||||||
xClient.socket = socket;
|
|
||||||
xClient.created_at = new Date();
|
|
||||||
Client.clients.set(id, xClient);
|
|
||||||
clients.set(id, xClient);
|
|
||||||
|
|
||||||
emit("connect", global, xClient);
|
|
||||||
|
|
||||||
let pingTimer = setInterval(()=> socket.ping('saQut') , 10_000);
|
|
||||||
|
|
||||||
socket.addListener("pong",validationText => {
|
termoutput && console.log("Web Socket Protocol is ready");
|
||||||
if(validationText.toString('utf8') != "saQut"){
|
|
||||||
socket.close();
|
http.addListener("upgrade", () => {
|
||||||
}
|
termoutput && console.log("HTTP Upgrading to WebSocket");
|
||||||
})
|
|
||||||
socket.addListener("message",({type,utf8Data}) => {
|
|
||||||
if(type == "utf8")
|
|
||||||
{
|
|
||||||
let json;
|
|
||||||
try{
|
|
||||||
json = JSON.parse(utf8Data);
|
|
||||||
emit('services', global, xClient, json);
|
|
||||||
let [payload, id, action] = json;
|
|
||||||
if(typeof id === "string")
|
|
||||||
{
|
|
||||||
action = id;
|
|
||||||
id = void 0;
|
|
||||||
};
|
|
||||||
emitService(global, xClient, id, payload, action);
|
|
||||||
}catch{
|
|
||||||
emit("messageError", global, xClient, utf8Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addListener("close",()=>{
|
wsServer = new websocket.server({
|
||||||
emit("disconnect", global, xClient);
|
httpServer: http,
|
||||||
Client.clients.delete(id);
|
autoAcceptConnections: true
|
||||||
clearInterval(pingTimer);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
wsServer.addListener("connect", (socket) => {
|
||||||
* @type {Map<string, Function[]>}
|
let client = new Client();
|
||||||
*/
|
let id = randomUUID();
|
||||||
let events = new Map();
|
socket.id = id;
|
||||||
/**
|
client.id = id;
|
||||||
* @type {Map<string, Function[]>}
|
client.socket = socket;
|
||||||
*/
|
client.created_at = new Date();
|
||||||
let services = []
|
Client.clients.set(id, client);
|
||||||
/**
|
|
||||||
*
|
EventEmitter.emit('connect', client);
|
||||||
* @param {string} event
|
|
||||||
* @param {(global:Map<string, any>, client:Client, data:any) => any} func
|
let pingTimer = setInterval(() => socket.ping('saQut'), 10_000);
|
||||||
*/
|
|
||||||
exports.addListener = (event, func) => {
|
socket.addListener("pong", (validationText) => {
|
||||||
if(!events.has(event))
|
if (validationText.toString('utf8') != "saQut") {
|
||||||
{
|
socket.close();
|
||||||
events.set(event,[]);
|
|
||||||
};
|
|
||||||
events.get(event).push(func);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} event
|
|
||||||
* @param {(data:{global:Map<string, any>, client:Client, message:any,response:Function,end:Function,next:Function}) => any} func
|
|
||||||
*/
|
|
||||||
exports.addService = (func) => {
|
|
||||||
services.push(func);
|
|
||||||
};
|
|
||||||
function emit(event,...args)
|
|
||||||
{
|
|
||||||
if(events.has(event))
|
|
||||||
{
|
|
||||||
for (const callback of events.get(event)) {
|
|
||||||
callback(...args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {Map} global
|
|
||||||
* @param {Client} local
|
|
||||||
* @param {number} id
|
|
||||||
* @param {{[key:string]:any}} payload
|
|
||||||
* @param {"R"|"S"} action [R]EQUEST flag or [S]TREAM flag
|
|
||||||
*/
|
|
||||||
async function emitService(global, client, id, payload, action)
|
|
||||||
{
|
|
||||||
let willContinue = false;
|
|
||||||
for (const callback of services) {
|
|
||||||
await callback({
|
|
||||||
message: payload,
|
|
||||||
action,
|
|
||||||
client,
|
|
||||||
global,
|
|
||||||
messageId: id,
|
|
||||||
response:(obj)=>{
|
|
||||||
id != undefined && client.send([obj, id, 'C']) // continue ([C]ONTINUE flag)
|
|
||||||
},
|
|
||||||
end:(obj)=>{
|
|
||||||
id != undefined && client.send([obj, id, 'E']) // stopped data stream (this channel) ([E]ND flag)
|
|
||||||
},
|
|
||||||
next:function(){
|
|
||||||
willContinue = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(willContinue === false) break;
|
|
||||||
}
|
socket.addListener("message", ({ type, utf8Data }) => {
|
||||||
};
|
if (type == "utf8") {
|
||||||
exports.websocket = wsServer;
|
try {
|
||||||
|
const json = JSON.parse(utf8Data);
|
||||||
|
const [message, id, action] = json;
|
||||||
|
|
||||||
|
let response;
|
||||||
|
|
||||||
|
if (typeof id === 'number' || typeof id === 'string') {
|
||||||
|
response = MessageRouter.handle(client, message);
|
||||||
|
|
||||||
|
if (action === 'R') {
|
||||||
|
client.send([response, id, 'E']);
|
||||||
|
} else if (action === 'S') {
|
||||||
|
client.send([response, id, 'C']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const result = MessageRouter.handle(client, message);
|
||||||
|
|
||||||
|
if (result && result.broadcast) {
|
||||||
|
EventEmitter.emit('broadcast', result.broadcast, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
EventEmitter.emit('messageError', client, utf8Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addListener("close", () => {
|
||||||
|
EventEmitter.emit('disconnect', client);
|
||||||
|
Client.clients.delete(id);
|
||||||
|
clearInterval(pingTimer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const on = EventEmitter.on;
|
||||||
|
const emit = EventEmitter.emit;
|
||||||
|
const off = EventEmitter.off;
|
||||||
|
|
||||||
|
const register = MessageRouter.register;
|
||||||
|
const handle = MessageRouter.handle;
|
||||||
|
|
||||||
|
exports.init = init;
|
||||||
|
exports.on = on;
|
||||||
|
exports.emit = emit;
|
||||||
|
exports.off = off;
|
||||||
|
exports.register = register;
|
||||||
|
exports.handle = handle;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const { Client } = require("./Client");
|
||||||
|
const { Room } = require("./Services/Room");
|
||||||
|
|
||||||
|
const apiKeys = new Map();
|
||||||
|
const webhooks = new Map();
|
||||||
|
|
||||||
|
function auth(req, res, next) {
|
||||||
|
const key = req.headers['x-api-key'];
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return res.status(401).json({ status: 'fail', message: 'API_KEY_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apiKeys.has(key)) {
|
||||||
|
return res.status(401).json({ status: 'fail', message: 'INVALID_API_KEY' });
|
||||||
|
}
|
||||||
|
|
||||||
|
req.server = apiKeys.get(key);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post('/auth/key', (req, res) => {
|
||||||
|
const { domain } = req.body;
|
||||||
|
|
||||||
|
if (!domain) {
|
||||||
|
return res.json({ status: 'fail', message: 'DOMAIN_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = require("crypto").randomUUID();
|
||||||
|
apiKeys.set(key, { domain, key });
|
||||||
|
|
||||||
|
res.json({ status: 'success', key });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/client/:id/send', auth, (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { pack } = req.body;
|
||||||
|
|
||||||
|
const client = Client.clients.get(id);
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
return res.json({ status: 'fail', message: 'CLIENT_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pack) {
|
||||||
|
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromServer = req.server;
|
||||||
|
client.send([{ from: 'server', fromServer, pack }, 'server/pack']);
|
||||||
|
|
||||||
|
res.json({ status: 'success' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/room/:id/send', auth, (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { pack, wom } = req.body;
|
||||||
|
|
||||||
|
const room = Room.rooms.get(id);
|
||||||
|
|
||||||
|
if (!room) {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pack) {
|
||||||
|
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromServer = req.server;
|
||||||
|
|
||||||
|
room.send(
|
||||||
|
[{ from: 'server', fromServer, pack, roomId: id }, 'server/pack/room'],
|
||||||
|
wom ? undefined : 'server',
|
||||||
|
() => true
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ status: 'success' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/room/create', auth, (req, res) => {
|
||||||
|
const { name, accessType, joinType, description, credential } = req.body;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
return res.json({ status: 'fail', message: 'NAME_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [, room] of Room.rooms) {
|
||||||
|
if (room.name === name) {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_ALREADY_EXISTS' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const room = new Room();
|
||||||
|
room.name = name;
|
||||||
|
room.accessType = accessType || 'public';
|
||||||
|
room.joinType = joinType || 'free';
|
||||||
|
room.description = description || '';
|
||||||
|
room.owner = { id: 'server', isServer: true };
|
||||||
|
|
||||||
|
if (credential) {
|
||||||
|
const { createHash } = require("crypto");
|
||||||
|
room.credential = createHash("sha256").update(credential).digest("hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
room.publish();
|
||||||
|
|
||||||
|
res.json({ status: 'success', room: room.toJSON() });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/room/:id/join', auth, (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { credential } = req.body;
|
||||||
|
|
||||||
|
const room = Room.rooms.get(id);
|
||||||
|
|
||||||
|
if (!room) {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType === 'lock') {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_LOCKED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.joinType === 'password') {
|
||||||
|
const { createHash } = require("crypto");
|
||||||
|
const hash = createHash(credential || '').digest("hex");
|
||||||
|
if (room.credential !== hash) {
|
||||||
|
return res.json({ status: 'fail', message: 'WRONG_PASSWORD' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeClient = {
|
||||||
|
id: 'server-joined',
|
||||||
|
isServer: true,
|
||||||
|
rooms: new Set(),
|
||||||
|
send: (msg) => {
|
||||||
|
const fromServer = req.server;
|
||||||
|
room.send([{ from: 'server', fromServer, ...msg[0] }, msg[1]], 'server');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
room.join(fakeClient);
|
||||||
|
|
||||||
|
res.json({ status: 'success', room: room.toJSON() });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/room/:id/leave', auth, (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const room = Room.rooms.get(id);
|
||||||
|
|
||||||
|
if (!room) {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeClient = { id: 'server-joined', isServer: true };
|
||||||
|
room.eject(fakeClient);
|
||||||
|
|
||||||
|
res.json({ status: 'success' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/room/:id', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const room = Room.rooms.get(id);
|
||||||
|
|
||||||
|
if (!room) {
|
||||||
|
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ status: 'success', room: room.toJSON() });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/rooms', (req, res) => {
|
||||||
|
const rooms = [];
|
||||||
|
|
||||||
|
for (const [id, room] of Room.rooms) {
|
||||||
|
rooms.push({
|
||||||
|
id,
|
||||||
|
name: room.name,
|
||||||
|
accessType: room.accessType,
|
||||||
|
joinType: room.joinType,
|
||||||
|
description: room.description,
|
||||||
|
clientCount: room.clients.size
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ status: 'success', rooms });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/clients', (req, res) => {
|
||||||
|
const clients = [];
|
||||||
|
|
||||||
|
for (const [id, client] of Client.clients) {
|
||||||
|
clients.push({
|
||||||
|
id,
|
||||||
|
rooms: [...client.rooms],
|
||||||
|
pairs: [...client.pairs]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ status: 'success', clients });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/webhook', auth, (req, res) => {
|
||||||
|
const { url, events } = req.body;
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return res.json({ status: 'fail', message: 'URL_REQUIRED' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = req.server;
|
||||||
|
webhooks.set(server.domain, { url, events: events || ['server/pack', 'server/pack/room'] });
|
||||||
|
|
||||||
|
res.json({ status: 'success' });
|
||||||
|
});
|
||||||
|
|
||||||
|
function triggerWebhook(event, data) {
|
||||||
|
for (const [, webhook] of webhooks) {
|
||||||
|
if (webhook.events.includes(event)) {
|
||||||
|
fetch(webhook.url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ event, data })
|
||||||
|
}).catch(console.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
module.exports.triggerWebhook = triggerWebhook;
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
require("./HTTPServer.js");
|
require("./HTTPServer.js");
|
||||||
require("./WebSocket.js");
|
|
||||||
|
const { http } = require("./HTTPServer");
|
||||||
|
|
||||||
|
const WebSocket = require("./WebSocket");
|
||||||
|
WebSocket.init(http);
|
||||||
|
|
||||||
require("./Services/YourID.js");
|
require("./Services/YourID.js");
|
||||||
require("./Services/Auth.js");
|
require("./Services/Auth.js");
|
||||||
|
|
@ -19,4 +23,4 @@ process.on('multipleResolves',(type, promise, value)=>{
|
||||||
});
|
});
|
||||||
process.on('warning',(err)=>{
|
process.on('warning',(err)=>{
|
||||||
console.log("Process warning", err)
|
console.log("Process warning", err)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
508
console/Hemex.js
508
console/Hemex.js
|
|
@ -1,508 +0,0 @@
|
||||||
function Hemex()
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
Hemex.EOL = "\n";
|
|
||||||
/**
|
|
||||||
* Hemex variable white space chars
|
|
||||||
* @type {Number[]}
|
|
||||||
*/
|
|
||||||
Hemex.WhiteSpace = [
|
|
||||||
9,10,11,12,13,32,133
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Current cursor position
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.offset = 0;
|
|
||||||
/**
|
|
||||||
* Mapping offset points
|
|
||||||
* @type {Number[]}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.offsetMap = [];
|
|
||||||
Hemex.prototype.beginPosition = function(){
|
|
||||||
this.offsetMap.push(
|
|
||||||
this.getLastPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Adding current position to offset map
|
|
||||||
*/
|
|
||||||
Hemex.prototype.acceptPosition = function(){
|
|
||||||
let t = this.offsetMap.pop();
|
|
||||||
this.setLastPosition(t)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get text range current and parent offsets
|
|
||||||
* @returns {[Number,Number]}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.positionRange = function(){
|
|
||||||
let len = this.offsetMap.length;
|
|
||||||
if(len == 0)
|
|
||||||
{
|
|
||||||
return [0,this.offset]
|
|
||||||
}else if(len == 1){
|
|
||||||
return [this.offset,this.offsetMap[len - 1]]
|
|
||||||
}else{
|
|
||||||
return [this.offsetMap[len - 2],this.offsetMap[len - 1]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get text range between current offset and parent offset
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getPositionRange = function(){
|
|
||||||
let u = this.positionRange();
|
|
||||||
return this.text.slice(u[0],u[1])
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Cancel current position and return to parent offset
|
|
||||||
*/
|
|
||||||
Hemex.prototype.rejectPosition = function(){
|
|
||||||
this.offsetMap.pop()
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get current layer of position from last offset of map
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getLastPosition = function(){
|
|
||||||
return this.offsetMap.length == 0 ? this.offset : this.offsetMap.slice(-1)[0]
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Set last position offset from offset map last layer
|
|
||||||
* @param {Number} n
|
|
||||||
*/
|
|
||||||
Hemex.prototype.setLastPosition = function(n){
|
|
||||||
if(this.offsetMap.length == 0)
|
|
||||||
this.offset = n
|
|
||||||
else this.offsetMap[this.offsetMap.length - 1] = n
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get current layer of position from last offset of map
|
|
||||||
* Some as getLastPosition()
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getOffset = function(){
|
|
||||||
return this.getLastPosition()
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Set last position offset from offset map last layer and return it value
|
|
||||||
* @param {Number} n
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.setOffset = function(n){
|
|
||||||
this.setLastPosition(n);
|
|
||||||
return this.getLastPosition()
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get text length
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.length = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hemex lexing data
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.text = "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set lexing data
|
|
||||||
* @param {String} text
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.setText = function(text){
|
|
||||||
this.offset = 0;
|
|
||||||
this.length = text.length;
|
|
||||||
this.offsetMap = [];
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get lexing all data
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getText = function(){
|
|
||||||
return this.text;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get one character from cursor
|
|
||||||
* @param {Number} n
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getChar = function(n){
|
|
||||||
return this.text.charAt(n?this.getOffset()+n:this.getOffset())
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Boolean
|
|
||||||
* @param {Number} n
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isChar = function(b){
|
|
||||||
return this.getChar() == b
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Dump all data from cursor position to end of char
|
|
||||||
* @param {Number} n
|
|
||||||
*/
|
|
||||||
Hemex.prototype.dump = function(n){
|
|
||||||
return this.text.slice(this.getOffset(),this.getOffset()+n)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Control coming end of line
|
|
||||||
* @returns {Bollean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isEnd = function(){
|
|
||||||
return this.length > this.getOffset()
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Forward one char
|
|
||||||
*/
|
|
||||||
Hemex.prototype.nextChar = function(){
|
|
||||||
this.setOffset(this.getOffset() + 1);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Forward n char
|
|
||||||
*/
|
|
||||||
Hemex.prototype.toChar = function(n){
|
|
||||||
this.setOffset(this.getOffset() + n);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Reading while end of line
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.getLine = function(){
|
|
||||||
return this.of(function(){
|
|
||||||
switch(this.getChar())
|
|
||||||
{
|
|
||||||
case Hemex.EOL: return false;
|
|
||||||
default: return true;
|
|
||||||
}
|
|
||||||
}.bind(this))
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Read all data until the function returns false
|
|
||||||
* @param {Boolean} p
|
|
||||||
* @param {(char:String)=>Boolean} e
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.of = function(e,p){
|
|
||||||
let k = [],count=0;
|
|
||||||
while(this.isEnd()){
|
|
||||||
if(e(p,count)) k.push(this.getChar());
|
|
||||||
else return k.join('');
|
|
||||||
count++;
|
|
||||||
this.nextChar();
|
|
||||||
};
|
|
||||||
return k.length == 0 ? false : k.join('')
|
|
||||||
}
|
|
||||||
Hemex.prototype.each = function(e,p){
|
|
||||||
let k = [];
|
|
||||||
while(this.isEnd())
|
|
||||||
if(!e(p)) return;
|
|
||||||
else this.nextChar();
|
|
||||||
}
|
|
||||||
Hemex.prototype.while = function(e,p){
|
|
||||||
let k = [];
|
|
||||||
while(this.isEnd())
|
|
||||||
if(!e(p)) return;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling for current char type
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isNumber = function(reverse){
|
|
||||||
let c = this.getChar().charCodeAt(0);
|
|
||||||
let result = c >= 48 && c <= 57;
|
|
||||||
return reverse ? !result : result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Read all data until char type is not number
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.readNumbers = function(reverse){
|
|
||||||
return this.of(this.isNumber.bind(this),reverse)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling for current char type
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isBigLetter = function(reverse){
|
|
||||||
let c = this.getChar().charCodeAt(0);
|
|
||||||
let result = c >= 97 && c <= 122;
|
|
||||||
return reverse ? !result : result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling for current char type
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isSmallLetter = function(reverse){
|
|
||||||
let c = this.getChar().charCodeAt(0);
|
|
||||||
let result = c >= 65 && c <= 90;
|
|
||||||
return reverse ? !result : result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling for current char type
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isLetter = function(reverse){
|
|
||||||
let result = this.isSmallLetter() || this.isBigLetter()
|
|
||||||
return reverse ? !result : result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Read all data until char type is not letter
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.readLetters = function(reverse){
|
|
||||||
return this.of(this.isLetter.bind(this),reverse)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling for current char type
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.isWhiteSpace = function(reverse){
|
|
||||||
let c = this.getChar(),ct = c.charCodeAt(0);
|
|
||||||
let result = (
|
|
||||||
c == '\n' ||
|
|
||||||
c == '\r' ||
|
|
||||||
c == '\t' ||
|
|
||||||
c == ' ' ||
|
|
||||||
Hemex.WhiteSpace.includes(ct)
|
|
||||||
)
|
|
||||||
return reverse ? !result : result;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Read all data until char type is not white space
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.readWhiteSpace = function(reverse){
|
|
||||||
return this.of(this.isWhiteSpace.bind(this),reverse)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling data
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.include = function(words,next){
|
|
||||||
this.beginPosition();
|
|
||||||
for(let i = 0; i<words.length; i++)
|
|
||||||
{
|
|
||||||
if(words[i] != this.getChar())
|
|
||||||
{
|
|
||||||
this.rejectPosition();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
this.nextChar();
|
|
||||||
};
|
|
||||||
if(next) this.acceptPosition();
|
|
||||||
else this.rejectPosition();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Controlling data
|
|
||||||
* @param {Boolean} reverse
|
|
||||||
* @returns {String|boolean}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.includes = function(arrays,accept){
|
|
||||||
this.beginPosition();
|
|
||||||
let flags = Array.from(arrays).fill(true);
|
|
||||||
let index = 0;
|
|
||||||
this.each(function(){
|
|
||||||
let stopLoop = true;
|
|
||||||
for(let T in arrays)
|
|
||||||
{
|
|
||||||
if(!flags[T] || arrays[T].length <= index) continue;
|
|
||||||
stopLoop = false;
|
|
||||||
flags[T] &= arrays[T][index] == this.getChar()
|
|
||||||
};
|
|
||||||
index++;
|
|
||||||
return !stopLoop && flags.filter(function(val){return val}).length != 0;
|
|
||||||
}.bind(this));
|
|
||||||
let result = arrays.filter(function(_,index){return flags[index]});
|
|
||||||
if(accept) this.acceptPosition();
|
|
||||||
else this.rejectPosition();
|
|
||||||
return result.length == 0 ? false : result
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Parsing number formats like; 12 75.1 0xE7 0b10 +3.46
|
|
||||||
* @returns {[String,Number]}
|
|
||||||
*/
|
|
||||||
Hemex.prototype.readNumber = function(){
|
|
||||||
let data = [];
|
|
||||||
let base = 10;
|
|
||||||
let nextDot = false;
|
|
||||||
let c = this.getChar();
|
|
||||||
if(this.isChar('0'))
|
|
||||||
{
|
|
||||||
this.nextChar();
|
|
||||||
switch(this.getChar())
|
|
||||||
{
|
|
||||||
case 'x':{
|
|
||||||
base = 16;
|
|
||||||
this.nextChar();
|
|
||||||
data.push('0x')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'b':{
|
|
||||||
base = 2;
|
|
||||||
this.nextChar();
|
|
||||||
data.push('0b')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
base = 8;
|
|
||||||
this.nextChar();
|
|
||||||
data.push('0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else base = 10;
|
|
||||||
this.each(()=>{
|
|
||||||
switch(c = this.getChar())
|
|
||||||
{
|
|
||||||
case '0':
|
|
||||||
case '1':{
|
|
||||||
data.push(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':{
|
|
||||||
if(base >= 8){
|
|
||||||
data.push(c);
|
|
||||||
break;
|
|
||||||
}else return false;
|
|
||||||
}
|
|
||||||
case '8':
|
|
||||||
case '9':{
|
|
||||||
if(base >= 10){
|
|
||||||
data.push(c);
|
|
||||||
break;
|
|
||||||
}else return false;
|
|
||||||
}
|
|
||||||
case 'A':
|
|
||||||
case 'a':
|
|
||||||
case 'B':
|
|
||||||
case 'b':
|
|
||||||
case 'C':
|
|
||||||
case 'c':
|
|
||||||
case 'D':
|
|
||||||
case 'd':
|
|
||||||
/* case 'E': case 'e': */
|
|
||||||
case 'F':
|
|
||||||
case 'f':{
|
|
||||||
if(base >= 16){
|
|
||||||
data.push(c);
|
|
||||||
break;
|
|
||||||
}else return false;
|
|
||||||
}
|
|
||||||
case '.':{
|
|
||||||
if(!nextDot){
|
|
||||||
if(data.length == 0){
|
|
||||||
data.push("0");
|
|
||||||
}else data.push(".");
|
|
||||||
nextDot = true;
|
|
||||||
isFloat = true;
|
|
||||||
}else{
|
|
||||||
throw new Error("Float number in Double dot");
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'E':
|
|
||||||
case 'e':{
|
|
||||||
if(this.getChar(1)!='+'){
|
|
||||||
if(base == 16){
|
|
||||||
data.push(c);
|
|
||||||
break;
|
|
||||||
}else return false;
|
|
||||||
};
|
|
||||||
if(data.length == 0){
|
|
||||||
this.rejectPosition();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
data.push('e');
|
|
||||||
this.nextChar();
|
|
||||||
if(this.getChar()=='+' || this.getChar()=='-'){
|
|
||||||
data.push(char());
|
|
||||||
this.nextChar();
|
|
||||||
};
|
|
||||||
let result = null;
|
|
||||||
this.each(()=>{
|
|
||||||
switch(this.getChar()){
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':{
|
|
||||||
data.push(this.getChar());
|
|
||||||
this.nextChar();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return true
|
|
||||||
});
|
|
||||||
return data.length == 0 ? false : [data.join(''),base]
|
|
||||||
}
|
|
||||||
|
|
||||||
Hemex.prototype.syntaxs = new Map();
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* @param {(hmx:Hemex, result: (result:any) => any,...args:any[]) => any} callback
|
|
||||||
*/
|
|
||||||
Hemex.prototype.syntax = function(name, callback){
|
|
||||||
this.syntaxs.set(name, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
Hemex.prototype.give = function(name, ...args){
|
|
||||||
let sandbox = this.syntaxs.get(name);
|
|
||||||
if(sandbox)
|
|
||||||
{
|
|
||||||
let res = undefined;
|
|
||||||
hmx.beginPosition();
|
|
||||||
if(sandbox(
|
|
||||||
this,
|
|
||||||
arg => {
|
|
||||||
res = arg
|
|
||||||
},
|
|
||||||
...args
|
|
||||||
))
|
|
||||||
{
|
|
||||||
hmx.acceptPosition();
|
|
||||||
return res;
|
|
||||||
}else{
|
|
||||||
hmx.rejectPosition();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Error} message
|
|
||||||
*/
|
|
||||||
Hemex.prototype.throw = function(message){
|
|
||||||
throw new Error(message)
|
|
||||||
};
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="tr">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<base href="/console/">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>WebSocket Terminal</title>
|
|
||||||
<style>
|
|
||||||
body{
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="terminal"></div>
|
|
||||||
<script src="xterm.js"></script>
|
|
||||||
<link rel="stylesheet" href="xterm.min.css">
|
|
||||||
<script src="./Hemex.js"></script>
|
|
||||||
<script src="lib.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
920
console/lib.js
920
console/lib.js
|
|
@ -1,920 +0,0 @@
|
||||||
var term = new Terminal({
|
|
||||||
cursorStyle: "underline",
|
|
||||||
cols: 180,
|
|
||||||
rows: 50
|
|
||||||
});
|
|
||||||
term.open(document.querySelector('#terminal'));
|
|
||||||
|
|
||||||
|
|
||||||
function newLine(t = true, prompt = true) {
|
|
||||||
t && term.write('\r\n');
|
|
||||||
cursorPosition = 0;
|
|
||||||
if(prompt)
|
|
||||||
{
|
|
||||||
term.write(DEFAULT_PROMPT);
|
|
||||||
cursorPosition += CLEAR_STYLING(DEFAULT_PROMPT).length;
|
|
||||||
}
|
|
||||||
resetUserSpace()
|
|
||||||
}
|
|
||||||
|
|
||||||
let waitms = ms => new Promise(ok => setTimeout(()=>ok(), ms));
|
|
||||||
|
|
||||||
let cursorPosition = 0;
|
|
||||||
let _buffer = [];
|
|
||||||
let COLOR_RESET = () => "\x1B[0m";
|
|
||||||
let COLOR_TEXT = (r,g,b) => `\x1B[38;2;${r};${g};${b}m`;
|
|
||||||
let COLOR_BACKGROUND = (r,g,b) => `\x1B[48;2;${r};${g};${b}m`;
|
|
||||||
|
|
||||||
let CURSOR_MOVE = (row, col) => `\x1B[${row};${col}H`;
|
|
||||||
let CURSOR_MOVE_UP = () => `\x1B[A`;
|
|
||||||
let CURSOR_MOVE_DOWN = () => `\x1B[B`;
|
|
||||||
let CURSOR_MOVE_RIGHT = () => `\x1B[C`;
|
|
||||||
let CURSOR_MOVE_LEFT = () => `\x1B[D`;
|
|
||||||
|
|
||||||
let CLEAR_SCREEN = () => `\x1B[2J`;
|
|
||||||
let CLEAR_LINE = () => `\x1B[2K`;
|
|
||||||
|
|
||||||
term.write(CURSOR_MOVE(2,1));
|
|
||||||
|
|
||||||
let CLEAR_STYLING = (text) => text.toString().replace(/\x1B(.*?)m/ig,'');
|
|
||||||
|
|
||||||
function writeHi()
|
|
||||||
{
|
|
||||||
term.write(
|
|
||||||
"Merhaba, "
|
|
||||||
+ COLOR_TEXT(0,255,0)
|
|
||||||
+ COLOR_BACKGROUND(32,32,32)
|
|
||||||
+ ' saQut RC Shell '
|
|
||||||
+ COLOR_RESET()
|
|
||||||
+ "\n\n\r"
|
|
||||||
);
|
|
||||||
|
|
||||||
newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
let userSpace = {
|
|
||||||
buffer: [],
|
|
||||||
paddingChars: 0,
|
|
||||||
size: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
function resetUserSpace()
|
|
||||||
{
|
|
||||||
userSpace = {
|
|
||||||
buffer: [],
|
|
||||||
paddingChars: 0,
|
|
||||||
size: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentLine()
|
|
||||||
{
|
|
||||||
return term
|
|
||||||
.buffer
|
|
||||||
.active
|
|
||||||
.getLine(
|
|
||||||
term
|
|
||||||
.buffer
|
|
||||||
.active
|
|
||||||
.cursorY
|
|
||||||
)
|
|
||||||
.translateToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let pausedInput = false;
|
|
||||||
let activeProgram = false;
|
|
||||||
|
|
||||||
term.onData(async chars => {
|
|
||||||
if(pausedInput)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(chars.length > 1)
|
|
||||||
{
|
|
||||||
return await DefaultProcess(chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(chars.length == 1)
|
|
||||||
{
|
|
||||||
if(activeProgram)
|
|
||||||
{
|
|
||||||
if(activeProgram._sys.idleChar)
|
|
||||||
{
|
|
||||||
return activeProgram._sys.idleChar.triggerChar(chars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return await DefaultProcess(chars);
|
|
||||||
}else if(activeProgram){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = [];
|
|
||||||
chars.toString().replace(/(\x1B.+?m)|(\x1B\[.+)|(.+)/igm, (_,a,b,c,d,e,f,g) => {
|
|
||||||
let charx = a || b || c;
|
|
||||||
if(charx)
|
|
||||||
{
|
|
||||||
if(/^(\x1B.*?m)$|^(\x1B\[.*)$/i.test(charx))
|
|
||||||
{
|
|
||||||
tokens.push([charx,])
|
|
||||||
}else{
|
|
||||||
tokens.push([,charx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const [code, chars] of tokens)
|
|
||||||
{
|
|
||||||
pausedInput = true;
|
|
||||||
if(code)
|
|
||||||
{
|
|
||||||
await DefaultProcess(code);
|
|
||||||
}else if(chars)
|
|
||||||
{
|
|
||||||
for (const char of chars.split(''))
|
|
||||||
{
|
|
||||||
await DefaultProcess(char);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pausedInput = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function DefaultProcess(char)
|
|
||||||
{
|
|
||||||
let contextchanged = false;
|
|
||||||
switch(char)
|
|
||||||
{
|
|
||||||
// Backspace
|
|
||||||
case '\x7f':
|
|
||||||
if(userSpace.paddingChars > 0)
|
|
||||||
{
|
|
||||||
term.write('\b \b');
|
|
||||||
cursorPosition--;
|
|
||||||
|
|
||||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars - 1);
|
|
||||||
let before = userSpace.buffer.slice(userSpace.paddingChars);
|
|
||||||
userSpace.buffer = [
|
|
||||||
...after,
|
|
||||||
...before
|
|
||||||
];
|
|
||||||
userSpace.paddingChars--;
|
|
||||||
userSpace.size--;
|
|
||||||
contextchanged = true;
|
|
||||||
|
|
||||||
term.write(before.join('')+" \b");
|
|
||||||
term.write(before.map(() => '\b').join(''));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// Enter
|
|
||||||
case '\r':
|
|
||||||
let command = userSpace.buffer.join('');
|
|
||||||
|
|
||||||
if(activeProgram && activeProgram._sys.idleReadline)
|
|
||||||
{
|
|
||||||
newLine(true, false);
|
|
||||||
return activeProgram._sys.triggerReadline(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsedCommand = ParseCommand(command);
|
|
||||||
if(parsedCommand != false)
|
|
||||||
{
|
|
||||||
let program = parsedCommand.find(e => e.type == 'commandname').data;
|
|
||||||
|
|
||||||
pausedInput = true;
|
|
||||||
|
|
||||||
newLine(true, false);
|
|
||||||
|
|
||||||
await ExecuteCommand(
|
|
||||||
program,
|
|
||||||
parsedCommand
|
|
||||||
);
|
|
||||||
pausedInput = false;
|
|
||||||
|
|
||||||
newLine(false);
|
|
||||||
}else{
|
|
||||||
newLine();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CURSOR_MOVE_LEFT():
|
|
||||||
if(userSpace.paddingChars > 0)
|
|
||||||
{
|
|
||||||
userSpace.paddingChars--;
|
|
||||||
term.write(CURSOR_MOVE_LEFT());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CURSOR_MOVE_UP():
|
|
||||||
case CURSOR_MOVE_DOWN():
|
|
||||||
break;
|
|
||||||
// Delete
|
|
||||||
case "\x1B[3~":
|
|
||||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
|
|
||||||
let before = userSpace.buffer.slice(userSpace.paddingChars + 1);
|
|
||||||
term.write(before.join('')+" \b");
|
|
||||||
term.write(before.map(() => '\b').join(''));
|
|
||||||
userSpace.buffer = [
|
|
||||||
...after,
|
|
||||||
...before
|
|
||||||
];
|
|
||||||
userSpace.size--;
|
|
||||||
contextchanged = true;
|
|
||||||
break;
|
|
||||||
case CURSOR_MOVE_RIGHT():
|
|
||||||
if(userSpace.paddingChars < userSpace.size)
|
|
||||||
{
|
|
||||||
term.write(CURSOR_MOVE_RIGHT());
|
|
||||||
userSpace.paddingChars++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
let printable = char.charCodeAt(0);
|
|
||||||
if(printable < 32)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// insert or append ?
|
|
||||||
if(userSpace.paddingChars == userSpace.size)
|
|
||||||
{
|
|
||||||
term.write(char);
|
|
||||||
|
|
||||||
userSpace.buffer = [
|
|
||||||
...userSpace.buffer.slice(0, userSpace.paddingChars),
|
|
||||||
char,
|
|
||||||
...userSpace.buffer.slice(userSpace.paddingChars),
|
|
||||||
];
|
|
||||||
|
|
||||||
userSpace.paddingChars++;
|
|
||||||
userSpace.size++;
|
|
||||||
cursorPosition++;
|
|
||||||
contextchanged = true;
|
|
||||||
|
|
||||||
}else{
|
|
||||||
|
|
||||||
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
|
|
||||||
let before = userSpace.buffer.slice(userSpace.paddingChars);
|
|
||||||
|
|
||||||
userSpace.buffer = [
|
|
||||||
...after,
|
|
||||||
char,
|
|
||||||
...before
|
|
||||||
];
|
|
||||||
|
|
||||||
term.write(char);
|
|
||||||
term.write(before.join(''));
|
|
||||||
term.write(before.map(() => '\b').join(''));
|
|
||||||
contextchanged = true;
|
|
||||||
userSpace.paddingChars++;
|
|
||||||
userSpace.size++;
|
|
||||||
cursorPosition++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!activeProgram)
|
|
||||||
{
|
|
||||||
contextchanged && COLORIZE_LINE();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function COLORIZE_LINE()
|
|
||||||
{
|
|
||||||
let before = userSpace.buffer.slice(0, userSpace.paddingChars);
|
|
||||||
let after = userSpace.buffer.slice(userSpace.paddingChars);
|
|
||||||
let coloredCommand = CommandColorize(before.concat(after).join(''));
|
|
||||||
|
|
||||||
if(coloredCommand)
|
|
||||||
{
|
|
||||||
// Cursoru en başa al
|
|
||||||
term.write(CURSOR_MOVE_LEFT().repeat(before.length));
|
|
||||||
term.write(coloredCommand);
|
|
||||||
term.write(CURSOR_MOVE_LEFT().repeat(after.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let USER_SPACE = "virtualhost";
|
|
||||||
let USER_NAME = [
|
|
||||||
147 + Math.random() * 25 | 0,
|
|
||||||
200 + Math.random() * 56 | 0,
|
|
||||||
0 + Math.random() * 256 | 0,
|
|
||||||
0 + Math.random() * 256 | 0
|
|
||||||
].join('.');
|
|
||||||
|
|
||||||
let DEFAULT_PROMPT = (
|
|
||||||
COLOR_RESET()
|
|
||||||
+ '['
|
|
||||||
+ COLOR_TEXT(0,255,0)
|
|
||||||
+ USER_SPACE
|
|
||||||
+ ' '
|
|
||||||
+ COLOR_TEXT(0,128,255)
|
|
||||||
+ USER_NAME
|
|
||||||
+ COLOR_RESET()
|
|
||||||
+ '] $ '
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
writeHi();
|
|
||||||
|
|
||||||
let _color_command = [255,255,255];
|
|
||||||
let _color_option = [128,128,255];
|
|
||||||
let _color_stringdata = [128,255,64];
|
|
||||||
|
|
||||||
function CommandColorize(command)
|
|
||||||
{
|
|
||||||
let hmx = new Hemex();
|
|
||||||
hmx.setText(command);
|
|
||||||
|
|
||||||
let output = [];
|
|
||||||
|
|
||||||
let commandBefore = hmx.of(() => !hmx.isLetter());
|
|
||||||
|
|
||||||
if(commandBefore.length != 0)
|
|
||||||
{
|
|
||||||
output.push(
|
|
||||||
COLOR_TEXT(255,255,255),
|
|
||||||
commandBefore,
|
|
||||||
COLOR_RESET()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!hmx.isLetter())
|
|
||||||
{
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
|
||||||
|
|
||||||
if(hmx.isEnd() && !hmx.isWhiteSpace())
|
|
||||||
{
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
output.push(
|
|
||||||
COLOR_TEXT(..._color_command),
|
|
||||||
commandName,
|
|
||||||
COLOR_RESET()
|
|
||||||
);
|
|
||||||
|
|
||||||
let all = [], stringstarted = false;
|
|
||||||
hmx.each(() => {
|
|
||||||
let char = hmx.getChar();
|
|
||||||
switch(char)
|
|
||||||
{
|
|
||||||
case '\'':
|
|
||||||
case '"':
|
|
||||||
case '`':{
|
|
||||||
if(stringstarted == false)
|
|
||||||
{
|
|
||||||
stringstarted = char;
|
|
||||||
all.push(COLOR_TEXT(..._color_stringdata), char)
|
|
||||||
}else if(stringstarted == char){
|
|
||||||
stringstarted = false;
|
|
||||||
all.push(char, COLOR_TEXT(..._color_option))
|
|
||||||
}else{
|
|
||||||
all.push(char)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
if(all.length == 0)
|
|
||||||
{
|
|
||||||
all.push(COLOR_TEXT(..._color_option), char);
|
|
||||||
}else{
|
|
||||||
all.push(char)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
output.push(all.join(''))
|
|
||||||
|
|
||||||
output.push(COLOR_RESET())
|
|
||||||
return output.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
function ParseCommand(command)
|
|
||||||
{
|
|
||||||
let hmx = new Hemex();
|
|
||||||
hmx.setText(command);
|
|
||||||
|
|
||||||
hmx.of(() => !hmx.isLetter());
|
|
||||||
|
|
||||||
if(!hmx.isLetter())
|
|
||||||
{
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let onlyArgs = false;
|
|
||||||
|
|
||||||
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
|
||||||
|
|
||||||
if(hmx.isEnd() && !hmx.isWhiteSpace())
|
|
||||||
{
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
hmx.readWhiteSpace();
|
|
||||||
|
|
||||||
let args = [];
|
|
||||||
|
|
||||||
let xargs = [], operation = 'option';
|
|
||||||
|
|
||||||
hmx.beginPosition();
|
|
||||||
hmx.while(()=>{
|
|
||||||
if(hmx.include('--') && operation == 'option')
|
|
||||||
{
|
|
||||||
xargs.length && (
|
|
||||||
args.push({
|
|
||||||
type: 'data',
|
|
||||||
data: xargs.join('')
|
|
||||||
}),
|
|
||||||
xargs = []
|
|
||||||
);
|
|
||||||
|
|
||||||
hmx.beginPosition();
|
|
||||||
|
|
||||||
hmx.include('--', true);
|
|
||||||
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
|
|
||||||
|
|
||||||
if(argumentkey == false)
|
|
||||||
{
|
|
||||||
hmx.rejectPosition();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.push({
|
|
||||||
type: "argument",
|
|
||||||
data: argumentkey
|
|
||||||
});
|
|
||||||
|
|
||||||
hmx.acceptPosition();
|
|
||||||
|
|
||||||
hmx.readWhiteSpace();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(hmx.include('-') && operation == 'option')
|
|
||||||
{
|
|
||||||
xargs.length && (
|
|
||||||
args.push({
|
|
||||||
type: 'data',
|
|
||||||
data: xargs.join('').trim()
|
|
||||||
}),
|
|
||||||
xargs = []
|
|
||||||
);
|
|
||||||
|
|
||||||
hmx.beginPosition();
|
|
||||||
hmx.nextChar();
|
|
||||||
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber());
|
|
||||||
|
|
||||||
if(argumentkey == false)
|
|
||||||
{
|
|
||||||
hmx.rejectPosition();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
argumentkey.split('').forEach(e => {
|
|
||||||
args.push({
|
|
||||||
type: "flag",
|
|
||||||
data: e
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
hmx.acceptPosition();
|
|
||||||
|
|
||||||
hmx.readWhiteSpace();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let char;
|
|
||||||
if((char = hmx.includes(['\'','"'])) && operation == 'option')
|
|
||||||
{
|
|
||||||
xargs.length && (
|
|
||||||
args.push({
|
|
||||||
type: 'data',
|
|
||||||
data: xargs.join('')
|
|
||||||
}),
|
|
||||||
xargs = []
|
|
||||||
);
|
|
||||||
|
|
||||||
let escapeChar = char[0];
|
|
||||||
hmx.nextChar();
|
|
||||||
|
|
||||||
let data = [];
|
|
||||||
|
|
||||||
hmx.while(() => {
|
|
||||||
let char = hmx.getChar();
|
|
||||||
switch(hmx.getChar())
|
|
||||||
{
|
|
||||||
case escapeChar:{
|
|
||||||
hmx.nextChar();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
data.push(char);
|
|
||||||
hmx.nextChar();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
args.push({
|
|
||||||
type: "string",
|
|
||||||
data: data.join('')
|
|
||||||
})
|
|
||||||
|
|
||||||
hmx.readWhiteSpace();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
xargs.push(hmx.getChar());
|
|
||||||
if(hmx.isWhiteSpace())
|
|
||||||
{
|
|
||||||
operation = 'option';
|
|
||||||
}else{
|
|
||||||
operation = 'data';
|
|
||||||
}
|
|
||||||
|
|
||||||
hmx.nextChar();
|
|
||||||
|
|
||||||
if(hmx.isEnd())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}else{
|
|
||||||
args.push({
|
|
||||||
type: "data",
|
|
||||||
data: xargs.join('')
|
|
||||||
})
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onlyArgs = hmx.getPositionRange();
|
|
||||||
hmx.acceptPosition();
|
|
||||||
|
|
||||||
return [{
|
|
||||||
type: "cmdline",
|
|
||||||
data: command
|
|
||||||
},{
|
|
||||||
type: "commandname",
|
|
||||||
data: commandName
|
|
||||||
},{
|
|
||||||
type: "arguments",
|
|
||||||
data: onlyArgs
|
|
||||||
}, ...args]
|
|
||||||
}
|
|
||||||
|
|
||||||
function generatepipe()
|
|
||||||
{
|
|
||||||
pausedInput = true;
|
|
||||||
|
|
||||||
let events = {};
|
|
||||||
|
|
||||||
let gen = {
|
|
||||||
stdin: {
|
|
||||||
setPaused(boolean){
|
|
||||||
pausedInput = Boolean(boolean);
|
|
||||||
},
|
|
||||||
async readline(){
|
|
||||||
gen.stdin.setPaused(false);
|
|
||||||
gen._sys.idleReadline = true;
|
|
||||||
return await new Promise(ok => {
|
|
||||||
events['readline'] = (result) => {
|
|
||||||
gen.stdin.setPaused(true);
|
|
||||||
gen._sys.idleReadline = false;
|
|
||||||
ok(result)
|
|
||||||
events['readline'] = null;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async getchar(){
|
|
||||||
gen.stdin.setPaused(false);
|
|
||||||
gen._sys.idleChar = true;
|
|
||||||
return await new Promise(ok => {
|
|
||||||
events['read'] = (result) => {
|
|
||||||
gen.stdin.setPaused(true);
|
|
||||||
gen._sys.idleChar = false;
|
|
||||||
events['read'] = null;
|
|
||||||
ok(result)
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stdout: {
|
|
||||||
async write(...args){
|
|
||||||
term.write(args.map(e => e.toString()).join(' '))
|
|
||||||
},
|
|
||||||
async writeln(...args){
|
|
||||||
term.writeln(args.map(e => e.toString()).join(' '))
|
|
||||||
},
|
|
||||||
COLOR_RESET,
|
|
||||||
COLOR_TEXT,
|
|
||||||
COLOR_BACKGROUND,
|
|
||||||
CURSOR_MOVE,
|
|
||||||
CURSOR_MOVE_UP,
|
|
||||||
CURSOR_MOVE_DOWN,
|
|
||||||
CURSOR_MOVE_RIGHT,
|
|
||||||
CURSOR_MOVE_LEFT,
|
|
||||||
CLEAR_SCREEN,
|
|
||||||
CLEAR_LINE,
|
|
||||||
CLEAR_STYLING
|
|
||||||
},
|
|
||||||
stderr: {
|
|
||||||
async write(...args){
|
|
||||||
term.write(COLOR_TEXT(255,0,0));
|
|
||||||
term.write(args.map(e => e.toString()).join(' '))
|
|
||||||
term.write(COLOR_RESET());
|
|
||||||
},
|
|
||||||
async writeln(...args){
|
|
||||||
term.write(COLOR_TEXT(255,0,0));
|
|
||||||
term.writeln(args.map(e => e.toString()).join(' '))
|
|
||||||
term.write(COLOR_RESET());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_sys:{
|
|
||||||
idleReadline: false,
|
|
||||||
idleChar: false,
|
|
||||||
triggerReadline(text){
|
|
||||||
if(events['readline']) events['readline'](text)
|
|
||||||
},
|
|
||||||
triggerChar(text){
|
|
||||||
if(events['read']) events['read'](text)
|
|
||||||
},
|
|
||||||
destroy(){
|
|
||||||
gen.stdin.setPaused(false);
|
|
||||||
events = void 0;
|
|
||||||
gen._sys.idleReadline = false;
|
|
||||||
gen._sys.idleChar = false;
|
|
||||||
gen._sys = void 0;
|
|
||||||
gen.stderr = void 0;
|
|
||||||
gen.stdin = void 0;
|
|
||||||
gen.stdout = void 0;
|
|
||||||
activeProgram = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
activeProgram = gen;
|
|
||||||
|
|
||||||
return gen;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ExecuteCommand(command, cmdline)
|
|
||||||
{
|
|
||||||
let Namespace = await CommandNamespace(command);
|
|
||||||
if(Namespace == null)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await new Promise(async (ok,reject) => {
|
|
||||||
resetUserSpace();
|
|
||||||
let program = new Namespace(generatepipe());
|
|
||||||
program.exit = () => ok();
|
|
||||||
try{
|
|
||||||
let t = program.main(cmdline);
|
|
||||||
if(t?.constructor?.name == 'AsyncFunction')
|
|
||||||
{
|
|
||||||
await t;
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
reject(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch(exception)
|
|
||||||
{
|
|
||||||
term.writeln('');
|
|
||||||
term.write(COLOR_TEXT(255,0,0));
|
|
||||||
term.writeln('Yazılım hatası:');
|
|
||||||
term.writeln('=========================');
|
|
||||||
term.write(COLOR_RESET());
|
|
||||||
term.writeln(exception.message);
|
|
||||||
term.writeln(exception.stack.replace(/\n/g,'\r\n'));
|
|
||||||
term.write(COLOR_TEXT(255,0,0));
|
|
||||||
term.writeln('=========================');
|
|
||||||
term.write(COLOR_RESET());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pausedInput = false;
|
|
||||||
activeProgram = void 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function InspectRepo()
|
|
||||||
{
|
|
||||||
if(CommandNamespace.repos.find(e => e.downloaded == false))
|
|
||||||
{
|
|
||||||
term.writeln(COLOR_RESET()+"\r\nUpdating repos....")
|
|
||||||
await wait(100);
|
|
||||||
}else{
|
|
||||||
term.writeln('');
|
|
||||||
}
|
|
||||||
for(const repo of CommandNamespace.repos)
|
|
||||||
{
|
|
||||||
if(repo.downloaded == false)
|
|
||||||
{
|
|
||||||
term.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
|
||||||
await wait(100);
|
|
||||||
let file = await DownloadRepoMetapack(repo.endpoint);
|
|
||||||
repo.packages = file;
|
|
||||||
term.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
|
||||||
await wait(100);
|
|
||||||
repo.downloaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
async function CommandNamespace(command)
|
|
||||||
{
|
|
||||||
let package = false;
|
|
||||||
if(!CommandNamespace.namespaces.has(command))
|
|
||||||
{
|
|
||||||
await InspectRepo();
|
|
||||||
for(const repo of CommandNamespace.repos)
|
|
||||||
{
|
|
||||||
for (const repopackage in repo.packages)
|
|
||||||
{
|
|
||||||
let packageName = repopackage;
|
|
||||||
let meta = repo.packages[repopackage];
|
|
||||||
let _command = meta.commands.find(_command => command == _command);
|
|
||||||
|
|
||||||
if(_command)
|
|
||||||
{
|
|
||||||
package = [packageName,meta];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(package == false)
|
|
||||||
{
|
|
||||||
term.writeln(COLOR_TEXT(255,0,0) + "'" + command + "' not found !" + COLOR_RESET());
|
|
||||||
}else{
|
|
||||||
term.writeln([
|
|
||||||
COLOR_TEXT(0,255,0),
|
|
||||||
package[0],
|
|
||||||
COLOR_TEXT(200,200,200),
|
|
||||||
" paketine ait olan ",
|
|
||||||
COLOR_TEXT(0,255,0),
|
|
||||||
command,
|
|
||||||
COLOR_TEXT(200,200,200),
|
|
||||||
" komutunu kullanabilmek için aşağıdaki komutunu çalıştırın"
|
|
||||||
].join('') +
|
|
||||||
"\r\n\n" +
|
|
||||||
CommandColorize(`load '${package[0]}' from 'official'`)+
|
|
||||||
"\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return CommandNamespace.namespaces.get(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function wait(ms)
|
|
||||||
{
|
|
||||||
return new Promise(ok => setTimeout(() => ok(),ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function DownloadRepoMetapack(endpoint)
|
|
||||||
{
|
|
||||||
let request = await fetch(endpoint,{
|
|
||||||
method: "get",
|
|
||||||
cache: "force-cache",
|
|
||||||
priority: "high",
|
|
||||||
redirect: "follow",
|
|
||||||
referrerPolicy: "no-referrer"
|
|
||||||
});
|
|
||||||
|
|
||||||
await wait(100);
|
|
||||||
|
|
||||||
try{
|
|
||||||
let response = await request.json();
|
|
||||||
return response;
|
|
||||||
}catch
|
|
||||||
{
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandNamespace.namespaces = new Map();
|
|
||||||
CommandNamespace.repos = [{
|
|
||||||
name: "official",
|
|
||||||
endpoint: new URL('/console/official/packages.json',window.location),
|
|
||||||
downloaded: false,
|
|
||||||
packages: {}
|
|
||||||
}];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SystemLoad{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
}
|
|
||||||
printhelp()
|
|
||||||
{
|
|
||||||
this.stdout.writeln('Kullanım şekli: ');
|
|
||||||
this.stdout.writeln('ExampleProgram paketini kurmak için aşağıdaki komutu yazmalısınız\r\n');
|
|
||||||
this.stdout.writeln(
|
|
||||||
CommandColorize(`load 'ExampleProgram'`)+"\r\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
let m = false, nargs = args.filter(({type}) => {
|
|
||||||
if(type == "arguments") return m = true, false;
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
|
|
||||||
let packname;
|
|
||||||
if(nargs[0].type == "string")
|
|
||||||
{
|
|
||||||
packname = nargs[0].data;
|
|
||||||
}else{
|
|
||||||
this.printhelp();
|
|
||||||
this.exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(CommandNamespace.repos.find(e => e.downloaded == false))
|
|
||||||
{
|
|
||||||
this.stdout.writeln(COLOR_RESET()+"\r\nUpdating repos....")
|
|
||||||
}else{
|
|
||||||
this.stdout.writeln('');
|
|
||||||
}
|
|
||||||
for(const repo of CommandNamespace.repos)
|
|
||||||
{
|
|
||||||
if(repo.downloaded == false)
|
|
||||||
{
|
|
||||||
this.stdout.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
|
||||||
await wait(100);
|
|
||||||
let file = await DownloadRepoMetapack(repo.endpoint.href);
|
|
||||||
if(file == null){
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
repo.packages = file;
|
|
||||||
this.stdout.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
|
|
||||||
await wait(100);
|
|
||||||
repo.downloaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let endpoints = CommandNamespace.repos.map(e => e.packages[packname] && e.packages[packname].endpoints.map(endpoint => new URL(endpoint,e.endpoint))).filter(e => !!e)
|
|
||||||
|
|
||||||
if(endpoints.length == 0)
|
|
||||||
{
|
|
||||||
term.writeln(COLOR_TEXT(255,0,0) + "'" + packname + "' repo not found !" + COLOR_RESET());
|
|
||||||
await wait(100);
|
|
||||||
this.exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const endpoint of endpoints[0])
|
|
||||||
{
|
|
||||||
let href = endpoint.href;
|
|
||||||
this.stdout.writeln(`Packet downloading :${COLOR_TEXT(0,255,0)} ${packname} ${COLOR_RESET()} ${endpoint.href}`);
|
|
||||||
await wait(100);
|
|
||||||
|
|
||||||
let request = await fetch(href,{
|
|
||||||
method: "get",
|
|
||||||
cache: "force-cache",
|
|
||||||
priority: "high",
|
|
||||||
redirect: "follow",
|
|
||||||
referrerPolicy: "no-referrer"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.stdout.writeln(`Packet unzip to :${COLOR_TEXT(0,255,0)} /mount/xpack/${packname}/ ${COLOR_RESET()}`);
|
|
||||||
await wait(100);
|
|
||||||
|
|
||||||
let script = await request.text();
|
|
||||||
|
|
||||||
try{
|
|
||||||
this.stdout.writeln(`Analyzing..`);
|
|
||||||
await new Promise(ok => setTimeout(() => ok(),1000));
|
|
||||||
(new Function(script))();
|
|
||||||
this.stdout.writeln(`Process success`);
|
|
||||||
await wait(100);
|
|
||||||
}catch(error){
|
|
||||||
debugger;
|
|
||||||
this.stderr.writeln(`Process error`);
|
|
||||||
await wait(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CommandNamespace.namespaces.set('load', SystemLoad);
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
class CommandJS{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
stderr = null;
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
this.stderr = pipe.stderr;
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
let commandline = args.find(e => e.type == "arguments");
|
|
||||||
if(commandline)
|
|
||||||
{
|
|
||||||
try{
|
|
||||||
let t = eval(commandline.data);
|
|
||||||
if(typeof t != "undefined")
|
|
||||||
{
|
|
||||||
this.stdout.writeln(JSON.stringify(t,null, ' ').replace(/\n/g,'\r\n'));
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
this.stderr.writeln(e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CommandJSRaw{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
stderr = null;
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
this.stderr = pipe.stderr;
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
let commandline = args.find(e => e.type == "arguments");
|
|
||||||
if(commandline)
|
|
||||||
{
|
|
||||||
let t = eval(commandline.data);
|
|
||||||
this.stdout.writeln(t);
|
|
||||||
}
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CommandNamespace.namespaces.set('js', CommandJS);
|
|
||||||
CommandNamespace.namespaces.set('jsecho', CommandJSRaw);
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
class CommandSet{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
stderr = null;
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
this.stderr = pipe.stderr;
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
this.stdout.writeln(JSON.stringify(args,null, ' ').replace(/\n/g,'\r\n'));
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
CommandNamespace.namespaces.set('set', CommandSet);
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"native": {
|
|
||||||
"endpoints": [
|
|
||||||
"./native/js.js",
|
|
||||||
"./native/set.js"
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"js",
|
|
||||||
"jsecho",
|
|
||||||
"set"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rtc": {
|
|
||||||
"endpoints": [
|
|
||||||
"./rtc/mwse-socket.js",
|
|
||||||
"./rtc/mwse-close.js"
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"mwse-socket",
|
|
||||||
"mwse-close"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
Oluşturulmuş cihazlar | device list
|
|
||||||
Yeni AudioContext | device create audio /dev/audio0
|
|
||||||
Yeni HTMLVideoElement | device create video /dev/video0
|
|
||||||
getUserMedia isteği | webcam 480x360 --audio /dev/audio0 --video /dev/video0
|
|
||||||
AudioContext sesi dışarı verme | device /dev/audio0 --output speaker
|
|
||||||
AudioContext sesi açma | device /dev/audio0 --value 100
|
|
||||||
Videoyu oynatma | lightplayer --source /dev/video0 --target popup
|
|
||||||
|
|
||||||
|
|
||||||
Tüm bağlantıları görme | rtc connections
|
|
||||||
RTC Oluşturma | rtc create "peer-0"
|
|
||||||
RTC offer verme | rtc "peer-0" create offer
|
|
||||||
RTC offer alma | rtc "peer-0" emit offer "offer message"
|
|
||||||
RTC answer verme | rtc "peer-0" create answer
|
|
||||||
RTC answer alma | rtc "peer-0" emit answer "answer message"
|
|
||||||
RTC answer verme | rtc "peer-0" create answer "answer message"
|
|
||||||
RTC candidate alma | rtc "peer-0" getcandidate --all
|
|
||||||
RTC candidate verme | rtc "peer-0" setcandidate
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
class MWSECloser{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
stderr = null;
|
|
||||||
|
|
||||||
endpoint = null;
|
|
||||||
static mwse = null;
|
|
||||||
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
this.stderr = pipe.stderr;
|
|
||||||
}
|
|
||||||
greenText(text){
|
|
||||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(0,255,0)}${text}${this.stdout.COLOR_RESET()}`);
|
|
||||||
}
|
|
||||||
redText(text){
|
|
||||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(255,0,0)}${text}${this.stdout.COLOR_RESET()}`);
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
let mwseConnector = CommandNamespace.namespaces.get('mwse-socket');
|
|
||||||
if(mwseConnector == null){
|
|
||||||
this.stderr.writeln(`mwse-socket yok`);
|
|
||||||
return this.exit();
|
|
||||||
}
|
|
||||||
if(mwseConnector.mwse == null){
|
|
||||||
this.stderr.writeln(`Açık bir soket bulunamadı`);
|
|
||||||
return this.exit();
|
|
||||||
}
|
|
||||||
if(mwseConnector.mwse.server?.disconnect == null){
|
|
||||||
this.stderr.writeln(`MWSE bağlayıcısında hata oluştu`);
|
|
||||||
return this.exit();
|
|
||||||
}
|
|
||||||
mwseConnector.mwse.server.disconnect();
|
|
||||||
this.greenText("MWSE socketi başarılı bir şekilde kapatıldı");
|
|
||||||
this.redText(`\n\n${mwseConnector.mwse.server.endpoint.host} sunucusuyla MWSE bağlantısı kesildi\n\n`);
|
|
||||||
return this.exit();
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
CommandNamespace.namespaces.set('mwse-close', MWSECloser);
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
class MWSECloser{
|
|
||||||
stdin = null;
|
|
||||||
stdout = null;
|
|
||||||
stderr = null;
|
|
||||||
|
|
||||||
endpoint = null;
|
|
||||||
static mwse = null;
|
|
||||||
|
|
||||||
constructor(pipe){
|
|
||||||
this.stdin = pipe.stdin;
|
|
||||||
this.stdout = pipe.stdout;
|
|
||||||
this.stderr = pipe.stderr;
|
|
||||||
}
|
|
||||||
greenText(text){
|
|
||||||
this.stdout.writeln(`${this.stdout.COLOR_TEXT(0,255,0)}${text}${this.stdout.COLOR_RESET()}`);
|
|
||||||
}
|
|
||||||
async main(args)
|
|
||||||
{
|
|
||||||
this.stdout.write("Endpoint: ");
|
|
||||||
let answer = await this.stdin.readline();
|
|
||||||
let url;
|
|
||||||
|
|
||||||
try{
|
|
||||||
url = new URL(answer)
|
|
||||||
}catch{
|
|
||||||
this.stderr.writeln('Bilinmeyen path yapısı');
|
|
||||||
return this.exit();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.greenText('Path doğrulandı');
|
|
||||||
|
|
||||||
if(url.protocol.toLowerCase() != 'wss:' && url.protocol.toLowerCase() != 'ws:'){
|
|
||||||
this.stderr.writeln('Bilinmeyen protokol');
|
|
||||||
return this.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.greenText('Şema doğrulandı');
|
|
||||||
|
|
||||||
this.endpoint = url;
|
|
||||||
|
|
||||||
try{
|
|
||||||
await this.scriptImport();
|
|
||||||
}catch{
|
|
||||||
this.stderr.writeln('MWSE bağlayıcısı indirilemedi');
|
|
||||||
return this.exit();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.greenText(`${url.host} üzerinden bağlanılıyor : ${url.href}`);
|
|
||||||
let mwse;
|
|
||||||
try{
|
|
||||||
mwse = new MWSE({
|
|
||||||
endpoint: url.href
|
|
||||||
});
|
|
||||||
|
|
||||||
await new Promise(ok => {
|
|
||||||
mwse.scope(async ()=>{
|
|
||||||
this.greenText(`\n\nMWSE üzerinden ${url.host} makinesine bağlısınız\n\n`);
|
|
||||||
ok();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
MWSECloser.mwse = mwse;
|
|
||||||
}catch{
|
|
||||||
this.stderr.writeln(`${url.host} makinesine bağlanırken bir sorun oluştu`);
|
|
||||||
return this.exit();
|
|
||||||
}finally{
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async scriptImport()
|
|
||||||
{
|
|
||||||
let t = new URL("script",this.endpoint);
|
|
||||||
if(t.protocol == 'wss:'){
|
|
||||||
t.protocol = 'https:';
|
|
||||||
}else{
|
|
||||||
t.protocol = 'http:';
|
|
||||||
}
|
|
||||||
let script = document.createElement("script");
|
|
||||||
await new Promise((ok,rej) => {
|
|
||||||
script.onload = () => ok();
|
|
||||||
script.onerror = () => rej();
|
|
||||||
script.setAttribute("src",t.href);
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
if(window.MWSE === void 0){
|
|
||||||
this.stderr.writeln('MWSE bağlayıcısı indirilemedi');
|
|
||||||
return this.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
CommandNamespace.namespaces.set('mwse-socket', MWSECloser);
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
||||||
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,25 +0,0 @@
|
||||||
import "./studio/window.js";
|
|
||||||
import { InputDevicesWindow } from "./studio/InputDevices.js";
|
|
||||||
import { Rooms } from "./studio/Rooms.js";
|
|
||||||
|
|
||||||
let mwse = new MWSE({
|
|
||||||
endpoint: "ws://localhost:7707"
|
|
||||||
});
|
|
||||||
|
|
||||||
mwse.scope(beginEngine);
|
|
||||||
|
|
||||||
async function beginEngine()
|
|
||||||
{
|
|
||||||
let me = await mwse.virtualPressure.allocAPIPAddress();
|
|
||||||
$(".network-id").text(me);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.openInputDevicesWindow = () => {
|
|
||||||
InputDevicesWindow.toggle();
|
|
||||||
InputDevicesWindow.bringToFront();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.openRoomsWindow = () => {
|
|
||||||
Rooms.toggle();
|
|
||||||
Rooms.bringToFront();
|
|
||||||
};
|
|
||||||
179
public/index.css
179
public/index.css
|
|
@ -1,179 +0,0 @@
|
||||||
html,body{
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
body{
|
|
||||||
background-color: #141414;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: system-ui;
|
|
||||||
}
|
|
||||||
*{
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.root{
|
|
||||||
gap: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.videolist{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(40vh, 1fr));
|
|
||||||
grid-template-rows: repeat(auto-fit, minmax(23vh, 1fr));
|
|
||||||
gap: 10px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.videolist .frame{
|
|
||||||
border: solid 2px white;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.videolist .frame > video{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
.videolist .frame.active{
|
|
||||||
border: solid 3px green;
|
|
||||||
box-shadow: 0px 0px 20px -10px green;
|
|
||||||
}
|
|
||||||
video{
|
|
||||||
image-rendering: pixelated;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background-color: #ffffff1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-container{
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.tool-container > .tools{
|
|
||||||
margin: auto;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.tool-container > .tools > button{
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
vertical-align: middle;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
.tool-container > .tools > button:not(:hover){
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.tool-container > .tools > button:hover{
|
|
||||||
background-color: rgba(255,255,255,0.2);
|
|
||||||
}
|
|
||||||
.tool-container > .tools > button i{
|
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-container > .namer{
|
|
||||||
flex: 0 0 auto;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 10px;
|
|
||||||
line-height: 2;
|
|
||||||
margin: 10px;
|
|
||||||
width: max-content;
|
|
||||||
padding: 0 10px;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: flex;
|
|
||||||
& > *{
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-container > .progress{
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
width: 250px;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
height: 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: white;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
& .progress-box {
|
|
||||||
height: 5px;
|
|
||||||
width: 50%;
|
|
||||||
background-color: red;
|
|
||||||
transition: width 0.05s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.controllist{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
gap: 12px;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctrl-btn {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: background 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctrl-btn:hover {
|
|
||||||
background: #f0f0f0;
|
|
||||||
border-color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctrl-btn .material-icons {
|
|
||||||
font-size: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo{
|
|
||||||
object-fit: contain;
|
|
||||||
max-height: 50px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="tr">
|
|
||||||
<head>
|
|
||||||
<base href="/stream/">
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>saQüt Video Streaming</title>
|
|
||||||
<script src="https://ws.saqut.com/script"></script>
|
|
||||||
<link rel="stylesheet" href="./index.css?v=42">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="root">
|
|
||||||
<div class="videolist">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;flex-direction: row;">
|
|
||||||
<div class="controllist">
|
|
||||||
<div style="flex:0 0 300px">
|
|
||||||
<img src="https://app.argist.com/img/argist-logo-yan.png" class="logo">
|
|
||||||
</div>
|
|
||||||
<div style="flex: 1 1 auto"></div>
|
|
||||||
<button class="ctrl-btn mic-on" title="Mikrofon Aç">
|
|
||||||
<span class="material-icons">mic</span>
|
|
||||||
</button>
|
|
||||||
<button class="ctrl-btn mic-off" title="Mikrofon Kapat" style="display:none">
|
|
||||||
<span class="material-icons">mic_off</span>
|
|
||||||
</button>
|
|
||||||
<button class="ctrl-btn cam-on" title="Kamera Aç" style="display:none">
|
|
||||||
<span class="material-icons">videocam</span>
|
|
||||||
</button>
|
|
||||||
<button class="ctrl-btn cam-off" title="Kamera Kapat">
|
|
||||||
<span class="material-icons">videocam_off</span>
|
|
||||||
</button>
|
|
||||||
<button class="ctrl-btn share-on" title="Ekran Paylaş">
|
|
||||||
<span class="material-icons">screen_share</span>
|
|
||||||
</button>
|
|
||||||
<button class="ctrl-btn share-off" title="Ekran Paylaşmayı Durdur" style="display:none">
|
|
||||||
<span class="material-icons">stop_screen_share</span>
|
|
||||||
</button>
|
|
||||||
<div style="flex: 1 1 auto"></div>
|
|
||||||
<div style="flex:0 0 300px"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="./index.js?v=42"></script>
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
352
public/index.js
352
public/index.js
|
|
@ -1,352 +0,0 @@
|
||||||
const url = new URL(window.location);
|
|
||||||
/**
|
|
||||||
* @type {import("./MWSE/index").default}
|
|
||||||
*/
|
|
||||||
let mwse;
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
let mySocketId;
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
let myIPAddress;
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
let myNumber;
|
|
||||||
/**
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
let roomid;
|
|
||||||
/**
|
|
||||||
* @type {import("./MWSE/Room").default}
|
|
||||||
*/
|
|
||||||
let room;
|
|
||||||
/**
|
|
||||||
* @type {MediaStream}
|
|
||||||
*/
|
|
||||||
let outgoingStream;
|
|
||||||
/**
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
let videoContainer = document.querySelector(".videolist");
|
|
||||||
let amaxb;
|
|
||||||
let vmaxb;
|
|
||||||
let rate;
|
|
||||||
let resulation;
|
|
||||||
let activePeers = {}
|
|
||||||
|
|
||||||
let ofscreencanvas = document.createElement("canvas");
|
|
||||||
|
|
||||||
function connect()
|
|
||||||
{
|
|
||||||
mwse = new MWSE({
|
|
||||||
endpoint: "wss://ws.saqut.com"
|
|
||||||
});
|
|
||||||
|
|
||||||
mwse.scope(beginEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
let peername = url.searchParams.get("user");
|
|
||||||
|
|
||||||
let interact = false;
|
|
||||||
|
|
||||||
setInterval(()=>{
|
|
||||||
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
|
|
||||||
},1000)
|
|
||||||
|
|
||||||
document.addEventListener("click",()=>{
|
|
||||||
interact = true;
|
|
||||||
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {HTMLVideoElement}
|
|
||||||
*/
|
|
||||||
let activeVideo;
|
|
||||||
|
|
||||||
function templateVideo(name, stream,infinitedMute, peername)
|
|
||||||
{
|
|
||||||
let t = new DOMParser().parseFromString(`
|
|
||||||
<div class="frame" data-name="${name}">
|
|
||||||
<video autoplay playsinline muted>
|
|
||||||
|
|
||||||
</video>
|
|
||||||
<div class="tool-container">
|
|
||||||
<div class="namer">
|
|
||||||
<div class="avatar">
|
|
||||||
<i class="material-icons">account_circle</i>
|
|
||||||
</div>
|
|
||||||
<div class="name">
|
|
||||||
${peername ?? "User"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tools">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-box">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`,"text/html");
|
|
||||||
|
|
||||||
let i = t.querySelector("video");
|
|
||||||
if(infinitedMute == true)
|
|
||||||
{
|
|
||||||
i.muted = 1;
|
|
||||||
}else if(interact == false)
|
|
||||||
{
|
|
||||||
i.muted = 1;
|
|
||||||
i.classList.add("soundon");
|
|
||||||
}
|
|
||||||
if(stream){
|
|
||||||
i.srcObject = stream;
|
|
||||||
bindAudioLevel(stream, t.querySelector(".progress-box"));
|
|
||||||
}
|
|
||||||
return t.querySelector("div");
|
|
||||||
}
|
|
||||||
function addVideoList(name, stream, peer, infinitedMute, name)
|
|
||||||
{
|
|
||||||
if(!videoContainer.querySelector(`[name="${name}"]`))
|
|
||||||
{
|
|
||||||
let video = templateVideo(name, stream, infinitedMute, name);
|
|
||||||
video.dataset.user = peer.socketId;
|
|
||||||
videoContainer.appendChild(video);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeVideoList(name)
|
|
||||||
{
|
|
||||||
if(videoContainer.querySelector(`[data-user="${name}"]`))
|
|
||||||
{
|
|
||||||
let k = videoContainer.querySelector(`[data-user="${name}"]`);
|
|
||||||
k.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function beginEngine()
|
|
||||||
{
|
|
||||||
let me = mwse.peer("me");
|
|
||||||
me.disablePairAuth();
|
|
||||||
mySocketId = me.socketId;
|
|
||||||
myIPAddress = await mwse.virtualPressure.allocAPIPAddress();
|
|
||||||
myNumber = await mwse.virtualPressure.allocAPNumber();
|
|
||||||
roomid = url.searchParams.get("room");
|
|
||||||
user = url.searchParams.get("user") ?? "User";
|
|
||||||
vmaxb = url.searchParams.get("vmaxb") ?? 32_0000
|
|
||||||
amaxb = url.searchParams.get("amaxb") ?? 2500000
|
|
||||||
resulation = url.searchParams.get("resulation") ?? 1
|
|
||||||
rate = url.searchParams.get("rate") ?? 10
|
|
||||||
|
|
||||||
if(!!roomid == 0)
|
|
||||||
{
|
|
||||||
let hash = window.crypto.randomUUID();
|
|
||||||
url.searchParams.set("room", hash);
|
|
||||||
url.searchParams.set("vmaxb", vmaxb);
|
|
||||||
url.searchParams.set("amaxb", amaxb);
|
|
||||||
url.searchParams.set("resulation", resulation);
|
|
||||||
url.searchParams.set("rate", rate);
|
|
||||||
url.searchParams.set("user", user);
|
|
||||||
window.location = url.href;
|
|
||||||
};
|
|
||||||
|
|
||||||
connectRoom(roomid);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
|
||||||
connect()
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
async function startOutgoingWebcam()
|
|
||||||
{
|
|
||||||
let mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
video: /*true*/{
|
|
||||||
advanced: [
|
|
||||||
{ width: { exact: 1920 } },
|
|
||||||
{ width: { exact: 1600 } },
|
|
||||||
{ width: { exact: 1366 } },
|
|
||||||
{ width: { exact: 1280 } },
|
|
||||||
{ width: { exact: 1024 } },
|
|
||||||
{ width: { exact: 900 } },
|
|
||||||
{ width: { exact: 800 } },
|
|
||||||
{ width: { exact: 640 } },
|
|
||||||
{ width: { exact: 320 } },
|
|
||||||
{ width: { exact: 240 } }
|
|
||||||
],
|
|
||||||
facingMode: "user"
|
|
||||||
},
|
|
||||||
audio: true
|
|
||||||
});
|
|
||||||
outgoingStream = mediaStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function connectRoom()
|
|
||||||
{
|
|
||||||
await startOutgoingWebcam();
|
|
||||||
|
|
||||||
room = mwse.room({
|
|
||||||
name: roomid,
|
|
||||||
joinType: "free",
|
|
||||||
accessType: "private",
|
|
||||||
description: "Private free joined room",
|
|
||||||
ifexistsJoin: true,
|
|
||||||
notifyActionEjected: true,
|
|
||||||
notifyActionInvite: false,
|
|
||||||
notifyActionJoined: true
|
|
||||||
});
|
|
||||||
|
|
||||||
await mwse.peer('me').info.set("name", peername);
|
|
||||||
|
|
||||||
await room.createRoom();
|
|
||||||
|
|
||||||
room.on("join", peer => IncomingPeer(peer, true));
|
|
||||||
room.on("eject", peer => OutgoingPeer(peer));
|
|
||||||
|
|
||||||
for (const peer of await room.fetchPeers()) {
|
|
||||||
IncomingPeer(peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
addVideoList("My Webcam",outgoingStream, mwse.peer("me"), true, peername);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import("./MWSE/Peer").default} peer
|
|
||||||
*/
|
|
||||||
async function IncomingPeer(peer,activeConnect)
|
|
||||||
{
|
|
||||||
await peer.info.fetch();
|
|
||||||
|
|
||||||
peer.createRTC({
|
|
||||||
iceCandidatePoolSize: 0
|
|
||||||
},[{
|
|
||||||
urls: "turn:20.166.82.187:3478",
|
|
||||||
username: "turnserver",
|
|
||||||
credential: "turnserver"
|
|
||||||
},{
|
|
||||||
urls: "stun:stun.l.google.com:19302"
|
|
||||||
}]);
|
|
||||||
|
|
||||||
if(peer.rtc.isPolite())
|
|
||||||
{
|
|
||||||
peer.rtc.connect();
|
|
||||||
}
|
|
||||||
peer.rtc.rtc.turboBitrate = 0;
|
|
||||||
peer.rtc.on('connected',() => {
|
|
||||||
if(peer.rtc.isPolite())
|
|
||||||
{
|
|
||||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
|
||||||
activePeers[peer.socketId] = peer;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
peer.rtc.on('disconnected',() => {
|
|
||||||
removeVideoList(peer.socketId, peer);
|
|
||||||
delete activePeers[peer.socketId];
|
|
||||||
});
|
|
||||||
peer.rtc.on("stream:added", ({stream,name}) => {
|
|
||||||
addVideoList(
|
|
||||||
peer.socketId,
|
|
||||||
stream,
|
|
||||||
peer,
|
|
||||||
void 0,
|
|
||||||
peer.info.get("name")
|
|
||||||
);
|
|
||||||
if(peer.rtc.isPolite() == false)
|
|
||||||
{
|
|
||||||
peer.rtc.sendStream(outgoingStream, "Webcam", {});
|
|
||||||
activePeers[peer.socketId] = peer;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {import("./MWSE/Peer").default} peer
|
|
||||||
*/
|
|
||||||
function OutgoingPeer(peer)
|
|
||||||
{
|
|
||||||
removeVideoList(peer.socketId, peer);
|
|
||||||
delete activePeers[peer.socketId];
|
|
||||||
}
|
|
||||||
|
|
||||||
let relative;
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
for(const [,peer] of Object.entries(activePeers))
|
|
||||||
{
|
|
||||||
let peerRtc = peer.rtc.rtc;
|
|
||||||
const senders = peerRtc.getSenders();
|
|
||||||
if(peerRtc.vturboBitrate !== 1)
|
|
||||||
{
|
|
||||||
const videoSender = senders.find(sender => sender.track?.kind === 'video');
|
|
||||||
if(videoSender){
|
|
||||||
const parameters = videoSender.getParameters();
|
|
||||||
parameters.encodings[0].maxBitrate = vmaxb;
|
|
||||||
parameters.encodings[0].scaleResolutionDownBy = resulation;
|
|
||||||
parameters.encodings[0].maxFramerate = rate;
|
|
||||||
videoSender.setParameters(parameters).then(() => {
|
|
||||||
peerRtc.vturboBitrate = 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(peerRtc.aturboBitrate !== 1)
|
|
||||||
{
|
|
||||||
const audioSender = senders.find(sender => sender.track?.kind === 'audio');
|
|
||||||
if(audioSender){
|
|
||||||
const parameters = audioSender.getParameters();
|
|
||||||
parameters.encodings[0].maxBitrate = amaxb;
|
|
||||||
audioSender.setParameters(parameters).then(() => {
|
|
||||||
peerRtc.aturboBitrate = 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},1000);
|
|
||||||
|
|
||||||
function bindAudioLevel(stream, element, options = {}) {
|
|
||||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
const source = audioCtx.createMediaStreamSource(stream);
|
|
||||||
const analyser = audioCtx.createAnalyser();
|
|
||||||
|
|
||||||
analyser.fftSize = 512;
|
|
||||||
analyser.smoothingTimeConstant = 0.1;
|
|
||||||
|
|
||||||
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
||||||
source.connect(analyser);
|
|
||||||
|
|
||||||
const track = stream.getAudioTracks()[0];
|
|
||||||
const bufferLength = analyser.frequencyBinCount;
|
|
||||||
|
|
||||||
function update()
|
|
||||||
{
|
|
||||||
if (track.readyState === 'ended' || !track.enabled) {
|
|
||||||
source.disconnect();
|
|
||||||
analyser.disconnect();
|
|
||||||
audioCtx.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
analyser.getByteFrequencyData(dataArray);
|
|
||||||
|
|
||||||
// Ortalama ses seviyesi hesapla
|
|
||||||
let sum = 0;
|
|
||||||
for (let i = 0; i < bufferLength; i++) {
|
|
||||||
sum += dataArray[i];
|
|
||||||
}
|
|
||||||
const avg = sum / bufferLength;
|
|
||||||
|
|
||||||
// Ortalama değeri % olarak hesapla (0-255 arası değer)
|
|
||||||
const volumePercent = Math.min(100, (avg / 255) * 100);
|
|
||||||
element.style.width = `${volumePercent * 2}%`;
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
/**
|
|
||||||
* =================================================================
|
|
||||||
* LİSANS BİLDİRİMİ -LGPL - LGPL-AU-EK
|
|
||||||
* =================================================================
|
|
||||||
* * Yazar (Original Author): Abdussamed ulutaş
|
|
||||||
* * İzinler (Permissions):
|
|
||||||
* ---------------------
|
|
||||||
* Bu kod, ticari veya kişisel amaçlarla serbestçe OKUNABILIR,
|
|
||||||
* KOPYALANABILIR, ÇOĞALTILABILIR, DEĞIŞTIRILEBILIR ve KULLANILABILIR.
|
|
||||||
* * * Koşul (Zorunlu Atıf Şartı - Attribution Requirement):
|
|
||||||
* ---------------------------------------------------
|
|
||||||
* Bu yazılımın temelindeki ANA ALGORITMA tamamen ve özgün bir şekilde değiştirilmediği sürece,
|
|
||||||
* "author by Abdussamed ulutaş" ifadesi kodun içerisinde MUTLAKA KORUNMALIDIR.
|
|
||||||
* * NOT: Algoritmanın temel mantığı korunarak yapılan küçük değişiklikler,
|
|
||||||
* optimizasyonlar veya arayüz entegrasyonları, bu atıf ifadesinin kaldırılması için yeterli sayılmaz.
|
|
||||||
* * * Sorumluluk Reddi (Disclaimer):
|
|
||||||
* ----------------------------
|
|
||||||
* Bu yazılım "olduğu gibi" (as-is) sağlanmıştır. Yazar, yazılımın kullanımından
|
|
||||||
* kaynaklanacak hiçbir zarardan sorumlu değildir.
|
|
||||||
* * =================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Aşağıdaki satır, atıf şartını yerine getiren zorunlu ifadedir ve kodun içinde kalmalıdır:
|
|
||||||
//
|
|
||||||
// author by Abdussamed ulutaş
|
|
||||||
//
|
|
||||||
// =================================================================
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Tüm dizinler herkese görünecek
|
|
||||||
- Dizinler ve dosyalar arayüzden tekrarlanamayacak
|
|
||||||
- Dosya ve klasör isimler aynı isimde olabilir
|
|
||||||
- Kimse bir başkasının dosyasını ve klasörünü silemez
|
|
||||||
|
|
||||||
- Yeni bir kişi bağlandığında dizin listesi eşitleniyor olacak
|
|
||||||
- Bağlanılan kişi ayrıldığında ona ait olan tüm dosya/dizin sistemi silinecek
|
|
||||||
*/
|
|
||||||
|
|
||||||
function é(rule)
|
|
||||||
{
|
|
||||||
if(é.element == null)
|
|
||||||
{
|
|
||||||
/** @type {HTMLStyleElement} */
|
|
||||||
é.element = document.createElement("style");
|
|
||||||
document.head.appendChild(é.element);
|
|
||||||
/** @type {CSSStyleSheet} */
|
|
||||||
é.sheet = é.element.sheet;
|
|
||||||
}
|
|
||||||
é.sheet.insertRule(rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
é`html,body{
|
|
||||||
background-color: black;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 14px;
|
|
||||||
> * {
|
|
||||||
box-sizing:border-box;
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
é`body{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
padding: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
}`;
|
|
||||||
|
|
||||||
é`@keyframes color-transition {
|
|
||||||
0% {
|
|
||||||
color: #c8c8c8;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: #757575;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
color: #c8c8c8;
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
|
|
||||||
/// UI
|
|
||||||
|
|
||||||
// <span class="material-icons">
|
|
||||||
|
|
||||||
const UIM = new class UI {
|
|
||||||
constructor(){
|
|
||||||
this.setup();
|
|
||||||
}
|
|
||||||
setup(){
|
|
||||||
this.setupOutContainer();
|
|
||||||
this.setupDirentContainer();
|
|
||||||
}
|
|
||||||
/** @type {JQuery} */
|
|
||||||
container = null;
|
|
||||||
setupOutContainer(){
|
|
||||||
this.container = $("<div>");
|
|
||||||
this.container.addClass("dip-fev");
|
|
||||||
é`.dip-fev{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(255,255,255,0.2);
|
|
||||||
position: relative;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: solid 1px rgba(255,255,255,0.1);
|
|
||||||
display: flex;
|
|
||||||
overflow: auto;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}`;
|
|
||||||
document.body.append(this.container.get(0));
|
|
||||||
}
|
|
||||||
/** @type {JQuery} */
|
|
||||||
direntbezier = null;
|
|
||||||
setupDirentContainer(){
|
|
||||||
this.direntbezier = $("<div>");
|
|
||||||
this.direntbezier.addClass("dip-bezier");
|
|
||||||
é`.dip-bezier{
|
|
||||||
flex: 0 0 auto;
|
|
||||||
background-color: rgba(255,255,255,0.2);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
padding: 5px;
|
|
||||||
& > .dip-bezier-icon{
|
|
||||||
padding: 3px;
|
|
||||||
animation: color-transition 1s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
& > .dip-bezier-disk{
|
|
||||||
color: white;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
& > .dip-bezier-object{
|
|
||||||
color: white;
|
|
||||||
padding: 3px 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 3px;
|
|
||||||
&:hover{
|
|
||||||
background-color: rgba(255,255,255,0.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > .dip-bezier-splitter{
|
|
||||||
margin: 5px 2px;
|
|
||||||
width: 1px;
|
|
||||||
background-color: rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
this.container.append(this.direntbezier);
|
|
||||||
|
|
||||||
this.setupBezier("Diskim",["Resimler","Yakalamalar","Benim yakalamalarım"]);
|
|
||||||
}
|
|
||||||
setupBezier(disk, folders){
|
|
||||||
let icon = $("<span>");
|
|
||||||
icon.addClass("dip-bezier-icon material-symbols-outlined");
|
|
||||||
icon.text("bigtop_updates");
|
|
||||||
this.direntbezier.append(icon);
|
|
||||||
|
|
||||||
let section = $("<div>");
|
|
||||||
section.addClass("dip-bezier-disk");
|
|
||||||
section.html(disk + " ://");
|
|
||||||
this.direntbezier.append(section);
|
|
||||||
|
|
||||||
folders.map((element, index, array) => {
|
|
||||||
let section = $("<div>");
|
|
||||||
section.addClass("dip-bezier-object");
|
|
||||||
section.text(element);
|
|
||||||
this.direntbezier.append(section);
|
|
||||||
if(index < array.length - 1)
|
|
||||||
{
|
|
||||||
let sectionPadding = $("<div>");
|
|
||||||
sectionPadding.addClass("dip-bezier-splitter");
|
|
||||||
this.direntbezier.append(sectionPadding);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/**
|
|
||||||
* =================================================================
|
|
||||||
* LİSANS BİLDİRİMİ - LGPL - LGPL-AU-EK
|
|
||||||
* =================================================================
|
|
||||||
* * Yazar (Original Author): Abdussamed ulutaş
|
|
||||||
* * İzinler (Permissions):
|
|
||||||
* ---------------------
|
|
||||||
* Bu kod, ticari veya kişisel amaçlarla serbestçe OKUNABILIR,
|
|
||||||
* KOPYALANABILIR, ÇOĞALTILABILIR, DEĞIŞTIRILEBILIR ve KULLANILABILIR.
|
|
||||||
* * * Koşul (Zorunlu Atıf Şartı - Attribution Requirement):
|
|
||||||
* ---------------------------------------------------
|
|
||||||
* Bu yazılımın temelindeki ANA ALGORITMA tamamen ve özgün bir şekilde değiştirilmediği sürece,
|
|
||||||
* "author by Abdussamed ulutaş" ifadesi kodun içerisinde MUTLAKA KORUNMALIDIR.
|
|
||||||
* * NOT: Algoritmanın temel mantığı korunarak yapılan küçük değişiklikler,
|
|
||||||
* optimizasyonlar veya arayüz entegrasyonları, bu atıf ifadesinin kaldırılması için yeterli sayılmaz.
|
|
||||||
* * * Sorumluluk Reddi (Disclaimer):
|
|
||||||
* ----------------------------
|
|
||||||
* Bu yazılım "olduğu gibi" (as-is) sağlanmıştır. Yazar, yazılımın kullanımından
|
|
||||||
* kaynaklanacak hiçbir zarardan sorumlu değildir.
|
|
||||||
* * =================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Aşağıdaki satır, atıf şartını yerine getiren zorunlu ifadedir ve kodun içinde kalmalıdır:
|
|
||||||
//
|
|
||||||
// author by Abdussamed ulutaş
|
|
||||||
//
|
|
||||||
// =================================================================
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>MWSE UI 24.3 Studio</title>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="max-width: 1200px;padding-top: 125px">
|
|
||||||
|
|
||||||
<h2 class="text-center">
|
|
||||||
MWSE UI 24.3
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<h1 class="text-center mb-5 mt-3 text-bold fw-bold">
|
|
||||||
<span>Ağ Adresiniz</span>
|
|
||||||
<span class="text-danger network-id">
|
|
||||||
#.#.#.#
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="position:fixed;left:0;right:0;bottom:0;background-color: rgba(0,0,0,.1);display: flex;flex-direction: row;gap: 5px">
|
|
||||||
<span class="bg-white d-inline-block m-1">
|
|
||||||
<button class="btn btn-outline-primary" onclick="openInputDevicesWindow()">
|
|
||||||
Giriş Aygıtları
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
<!--span class="bg-white d-inline-block m-1">
|
|
||||||
<button class="btn btn-outline-primary" onclick="openOutputDevicesWindow()">
|
|
||||||
Çıkış Aygıtları
|
|
||||||
</button>
|
|
||||||
</span-->
|
|
||||||
<span class="bg-white d-inline-block m-1">
|
|
||||||
<button class="btn btn-outline-primary" onclick="openRoomsWindow()">
|
|
||||||
Odalar
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
<span class="bg-white d-inline-block m-1">
|
|
||||||
<button class="btn btn-outline-primary" onclick="openPeersWindow()">
|
|
||||||
Cihazlar
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<script src="https://ws.saqut.com/script"></script>
|
|
||||||
<script src="./core.js" type="module"></script>
|
|
||||||
<style>
|
|
||||||
html,body{
|
|
||||||
margin: 0;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url(./studio/background.jpg);
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
*{
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #7a7a7a #1e1e1e;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
|
||||||
background: #1e1e1e;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #7a7a7a;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: #aaaaaa;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,398 +0,0 @@
|
||||||
import DWindow from "./window.js";
|
|
||||||
import { AddCell, FilterCell, memory, RemoveCell } from "./sdb.js";
|
|
||||||
|
|
||||||
export const InputDevicesWindow = new class InputDevicesWindow extends DWindow{
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.initContent();
|
|
||||||
this.headtext.text("Giriş Aygıtları");
|
|
||||||
this.hide();
|
|
||||||
this.panel.css("min-width","640px");
|
|
||||||
this.panel.css("min-height","250px");
|
|
||||||
this.panel.css("width","max-content");
|
|
||||||
this.initEvent();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @type {JQuery<HTMLElement>}
|
|
||||||
*/
|
|
||||||
table = null;
|
|
||||||
initContent(){
|
|
||||||
this.table = $(`
|
|
||||||
<table class="table table-sm table-bordered table-striped text-nowrap">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Cihaz Türü</th>
|
|
||||||
<th>Sağlanan Kanallar</th>
|
|
||||||
<th>Akış ID</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan="4">
|
|
||||||
<button class="btn btn-success w-100 newdevice">
|
|
||||||
Yeni Giriş Cihazı Ekle
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
`);
|
|
||||||
|
|
||||||
this.updateDevices();
|
|
||||||
|
|
||||||
this.content.append(this.table);
|
|
||||||
}
|
|
||||||
initEvent(){
|
|
||||||
this.content.find(".newdevice").on('click', this.generateDevice.bind(this));
|
|
||||||
this.addEventListener("close",()=>{
|
|
||||||
this.hide();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
updateDevices(){
|
|
||||||
let tbody = this.table.find("tbody");
|
|
||||||
tbody.text('');
|
|
||||||
for (const cell of FilterCell("rawstream", () => true))
|
|
||||||
{
|
|
||||||
let tr = $(`
|
|
||||||
<tr class="text-nowrap">
|
|
||||||
<td>${cell.source}</td>
|
|
||||||
<td>${cell.kind}</td>
|
|
||||||
<td>${cell.payload.id}</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-outline-primary action-view">Görüntüle</button>
|
|
||||||
<button class="btn btn-outline-danger action-close">Kapat</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
tr.attr("data-declare", cell.payload.id);
|
|
||||||
cell.addEventListener("stop",() => tr.remove());
|
|
||||||
tbody.append(tr);
|
|
||||||
}
|
|
||||||
tbody.find(".action-view").on('click', e => this.viewSource($(e.target)));
|
|
||||||
tbody.find(".action-close").on('click', e => this.closeSource($(e.target)));
|
|
||||||
}
|
|
||||||
closeSource(btn){
|
|
||||||
let streamid = btn.closest("tr").attr("data-declare");
|
|
||||||
for (const brain of FilterCell("rawstream",e => e.payload.id == streamid)) {
|
|
||||||
brain.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewSource(btn){
|
|
||||||
let streamid = btn.closest("tr").attr("data-declare");
|
|
||||||
/**
|
|
||||||
* @type {MediaStream}
|
|
||||||
*/
|
|
||||||
let p = null;
|
|
||||||
for (const rawstream of FilterCell("rawstream",e => e.payload.id == streamid)) {
|
|
||||||
p = rawstream.payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modal = new ViewSourceDialog(p.getTracks()[0]);
|
|
||||||
modal.enableDialog(this);
|
|
||||||
modal.bringToFront();
|
|
||||||
}
|
|
||||||
generateDevice(){
|
|
||||||
let modal = new AddDeviceDialog();
|
|
||||||
modal.enableDialog(this);
|
|
||||||
modal.bringToFront();
|
|
||||||
modal.addEventListener("stream:added",()=>{
|
|
||||||
this.updateDevices();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StreamRecord extends EventTarget {
|
|
||||||
lockedby = 0;
|
|
||||||
stop(){
|
|
||||||
this.payload.getTracks().forEach(e => e.stop());
|
|
||||||
this.dispatchEvent(new Event("stop"));
|
|
||||||
}
|
|
||||||
isLocked(){
|
|
||||||
return this.lockedby != 0;
|
|
||||||
}
|
|
||||||
lock(){
|
|
||||||
this.lockedby++;
|
|
||||||
}
|
|
||||||
unlock(){
|
|
||||||
this.lockedby--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddDeviceDialog extends DWindow {
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.initContent();
|
|
||||||
this.headtext.text("Aygıt Ekleme Sihirbazı");
|
|
||||||
this.addEventListener('close',()=>{
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
initContent(){
|
|
||||||
let table = $(`
|
|
||||||
<div style="display:flex;flex-direction:column;gap:5px">
|
|
||||||
<button class="btn btn-success w-100 action-sdcam">
|
|
||||||
SD Kamara Ekle
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success w-100 action-hdcam">
|
|
||||||
HD Kamara Ekle
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success w-100 action-mic">
|
|
||||||
Mikrofon Ekle
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success w-100 action-displaycam">
|
|
||||||
Ekran Görüntüsü Ekle
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`);
|
|
||||||
|
|
||||||
table.find(".action-sdcam").on("click",this.sdcam.bind(this));
|
|
||||||
table.find(".action-hdcam").on("click",this.hdcam.bind(this));
|
|
||||||
table.find(".action-mic").on("click",this.mic.bind(this));
|
|
||||||
table.find(".action-displaycam").on("click",this.displaycam.bind(this));
|
|
||||||
this.content.append(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
async sdcam(){
|
|
||||||
let stream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
video: {
|
|
||||||
advanced: [
|
|
||||||
{ width: { exact: 640 } },
|
|
||||||
{ width: { exact: 320 } },
|
|
||||||
{ width: { exact: 240 } }
|
|
||||||
],
|
|
||||||
facingMode: "user"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AddCell("rawstream", new class extends StreamRecord {
|
|
||||||
source = "Kamera";
|
|
||||||
kind = "Görüntü";
|
|
||||||
payload = stream;
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.onEnded(() => {
|
|
||||||
this.dispatchEvent(new Event("stop"));
|
|
||||||
})
|
|
||||||
this.addEventListener("stop",() => {
|
|
||||||
RemoveCell("rawstream",cell => {
|
|
||||||
return cell.payload.id == stream.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onEnded(event){
|
|
||||||
/** @type {MediaStreamTrack} */
|
|
||||||
let track;
|
|
||||||
stream.getVideoTracks().forEach(e => track = e);
|
|
||||||
if(track)
|
|
||||||
{
|
|
||||||
track.addEventListener("ended",event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.dispatchEvent(new Event("stream:added"));
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
async hdcam(){
|
|
||||||
let stream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
video: {
|
|
||||||
advanced: [
|
|
||||||
{ width: { exact: 1920 } },
|
|
||||||
{ width: { exact: 1600 } },
|
|
||||||
{ width: { exact: 1366 } },
|
|
||||||
{ width: { exact: 1280 } },
|
|
||||||
{ width: { exact: 1024 } },
|
|
||||||
{ width: { exact: 900 } },
|
|
||||||
{ width: { exact: 800 } },
|
|
||||||
{ width: { exact: 640 } },
|
|
||||||
{ width: { exact: 320 } },
|
|
||||||
{ width: { exact: 240 } }
|
|
||||||
],
|
|
||||||
facingMode: "user"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
AddCell("rawstream", new class extends StreamRecord {
|
|
||||||
source = "Kamera";
|
|
||||||
kind = "Görüntü";
|
|
||||||
payload = stream;
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.onEnded(() => {
|
|
||||||
this.dispatchEvent(new Event("stop"));
|
|
||||||
})
|
|
||||||
this.addEventListener("stop",() => {
|
|
||||||
RemoveCell("rawstream",cell => {
|
|
||||||
return cell.payload.id == stream.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onEnded(event){
|
|
||||||
/** @type {MediaStreamTrack} */
|
|
||||||
let track;
|
|
||||||
stream.getVideoTracks().forEach(e => track = e);
|
|
||||||
if(track)
|
|
||||||
{
|
|
||||||
track.addEventListener("ended",event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.dispatchEvent(new Event("stream:added"));
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
async mic(){
|
|
||||||
let stream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
audio: true
|
|
||||||
});
|
|
||||||
AddCell("rawstream", new class extends StreamRecord {
|
|
||||||
source = "Kamera";
|
|
||||||
kind = "Ses";
|
|
||||||
payload = stream;
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.onEnded(() => {
|
|
||||||
this.dispatchEvent(new Event("stop"));
|
|
||||||
})
|
|
||||||
this.addEventListener("stop",() => {
|
|
||||||
RemoveCell("rawstream",cell => {
|
|
||||||
return cell.payload.id == stream.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onEnded(event){
|
|
||||||
/** @type {MediaStreamTrack} */
|
|
||||||
let track;
|
|
||||||
stream.getAudioTracks().forEach(e => track = e);
|
|
||||||
if(track)
|
|
||||||
{
|
|
||||||
track.addEventListener("ended",event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.dispatchEvent(new Event("stream:added"));
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
async displaycam(){
|
|
||||||
let stream = await navigator.mediaDevices.getDisplayMedia({
|
|
||||||
video: true
|
|
||||||
});
|
|
||||||
AddCell("rawstream", new class extends StreamRecord{
|
|
||||||
source = "Ekran";
|
|
||||||
kind = "Görüntü";
|
|
||||||
payload = stream;
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.onEnded(() => {
|
|
||||||
this.dispatchEvent(new Event("stop"));
|
|
||||||
})
|
|
||||||
this.addEventListener("stop",() => {
|
|
||||||
RemoveCell("rawstream",cell => {
|
|
||||||
return cell.payload.id == stream.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onEnded(event){
|
|
||||||
/** @type {MediaStreamTrack} */
|
|
||||||
let track;
|
|
||||||
stream.getTracks().forEach(e => track = e);
|
|
||||||
if(track)
|
|
||||||
{
|
|
||||||
track.addEventListener("ended",event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.dispatchEvent(new Event("stream:added"));
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class ViewSourceDialog extends DWindow {
|
|
||||||
/**
|
|
||||||
* @type {MediaStreamTrack}
|
|
||||||
*/
|
|
||||||
track = null;
|
|
||||||
extra = {};
|
|
||||||
constructor(track){
|
|
||||||
super();
|
|
||||||
this.headtext.text("Giriş Kaynağı Görüntüleme");
|
|
||||||
this.addEventListener('close',()=>{
|
|
||||||
this.disableDialog();
|
|
||||||
this.exit();
|
|
||||||
})
|
|
||||||
this.track = track;
|
|
||||||
this.initContent();
|
|
||||||
}
|
|
||||||
initContent(){
|
|
||||||
if(this.track.kind == "video")
|
|
||||||
{
|
|
||||||
this.initVideoContent();
|
|
||||||
}else{
|
|
||||||
this.initAudioContent();
|
|
||||||
}
|
|
||||||
this.table = $(`<table class="table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Track ID</td>
|
|
||||||
<td>${this.track.id}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Data Type</td>
|
|
||||||
<td>${this.track.kind}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Status</td>
|
|
||||||
<td>${this.track.enabled ? "Active" : "Passive"}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Track Label</td>
|
|
||||||
<td>${this.track.label}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>`);
|
|
||||||
this.content.append(this.table);
|
|
||||||
}
|
|
||||||
initVideoContent(){
|
|
||||||
let player = $(`<video
|
|
||||||
style="height: 450px"
|
|
||||||
controls
|
|
||||||
/>`);
|
|
||||||
let stream = new MediaStream();
|
|
||||||
stream.addTrack(this.track);
|
|
||||||
player.on('canplay', e => e.target.play());
|
|
||||||
player.get(0).srcObject = stream;
|
|
||||||
this.content.append(player);
|
|
||||||
player.on('play', e => {
|
|
||||||
this.table.find("tbody").append(`
|
|
||||||
<tr>
|
|
||||||
<td>Video Width</td>
|
|
||||||
<td>${e.target.videoWidth}px</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
this.table.find("tbody").append(`
|
|
||||||
<tr>
|
|
||||||
<td>Video Height</td>
|
|
||||||
<td>${e.target.videoHeight}px</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
initAudioContent(){
|
|
||||||
let player = $(`<audio
|
|
||||||
style="width: 500px"
|
|
||||||
controls
|
|
||||||
/>`);
|
|
||||||
let stream = new MediaStream();
|
|
||||||
stream.addTrack(this.track);
|
|
||||||
player.get(0).srcObject = stream;
|
|
||||||
player.on('canplay', e => e.target.play());
|
|
||||||
this.content.append(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import DWindow from "./window.js";
|
|
||||||
import { AddCell, FilterCell, memory, RemoveCell } from "./sdb.js";
|
|
||||||
|
|
||||||
export const Rooms = new class Rooms extends DWindow{
|
|
||||||
constructor(){
|
|
||||||
super();
|
|
||||||
this.emptyConnect();
|
|
||||||
this.headtext.text("MWSE Ağları");
|
|
||||||
this.hide();
|
|
||||||
this.panel.css("min-width","640px");
|
|
||||||
this.addEventListener("close",()=>{
|
|
||||||
this.hide();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
emptyConnect(){
|
|
||||||
this.content.append($(`
|
|
||||||
<h4 class="text-white text-center">MWSE Ağları</h4>
|
|
||||||
`),$(`
|
|
||||||
<h6 class="text-white text-center">Herhangi bir ağa bağlı değilsiniz</h6>
|
|
||||||
`));
|
|
||||||
|
|
||||||
|
|
||||||
this.content.append($(`
|
|
||||||
<h3 class="text-white text-center" style="margin-top: 50px">
|
|
||||||
Ağ Oluştur
|
|
||||||
</h3>
|
|
||||||
`));
|
|
||||||
|
|
||||||
let createinput = $(`
|
|
||||||
<input type="text" class="form-control" placeholder="Oluşturulacak Oda Adı" style="line-height:2;font-size: 2em">
|
|
||||||
`);
|
|
||||||
|
|
||||||
let createbutton = $(`
|
|
||||||
<button class="btn btn-lg btn-success d-block m-auto mt-3">Oluştur</button>
|
|
||||||
`);
|
|
||||||
|
|
||||||
this.content.append(createinput,createbutton);
|
|
||||||
|
|
||||||
this.content.append($(`
|
|
||||||
<h3 class="text-white text-center" style="margin-top: 50px">
|
|
||||||
Ağa Katıl
|
|
||||||
</h3>
|
|
||||||
`));
|
|
||||||
|
|
||||||
let joininput = $(`
|
|
||||||
<input type="text" class="form-control" placeholder="Katılacak Oda Adı" style="line-height:2;font-size: 2em">
|
|
||||||
`);
|
|
||||||
|
|
||||||
|
|
||||||
let joinbutton = $(`
|
|
||||||
<button class="btn btn-lg btn-success d-block m-auto mt-3">Katıl</button>
|
|
||||||
`);
|
|
||||||
this.content.append(joininput,joinbutton);
|
|
||||||
|
|
||||||
this.content.append($(`
|
|
||||||
<span class="text-white" style="margin-top: 50px;max-width:100%;font-size: 0.9em;line-height: 1;display: block;text-shadow: 0px 0px 4px black;">
|
|
||||||
Aynı ağdaki kullanıcılar birbirini görecek ve akışlarını paylaşabilecektir.
|
|
||||||
Verilerinizi, bilgilerinizi ve akışlarınızı paylaşmakta tamamen özgürsünüz.
|
|
||||||
Sistem tamamen deneysel teknoloji araştırma ve geliştirme üzerine kuruludur.
|
|
||||||
Sistem kullanılarak yapılan tüm iyi ve kötü amaçlı eylemlerden kullanıcı sorumludur.
|
|
||||||
</span>
|
|
||||||
`));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 660 KiB |
|
|
@ -1,162 +0,0 @@
|
||||||
const style = document.createElement("style");
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
export default class CSSOM {
|
|
||||||
/** @type {Map<string, CSSOM>} */
|
|
||||||
static rules = new Map();
|
|
||||||
/** @type {Map<string, CSSRuleList>} */
|
|
||||||
static cRules = new Map();
|
|
||||||
/**
|
|
||||||
* @returns {CSSOM}
|
|
||||||
*/
|
|
||||||
static get(name){
|
|
||||||
return CSSOM.rules.get(name);
|
|
||||||
}
|
|
||||||
/** @returns {CSSRule} */
|
|
||||||
static findRule(selector){
|
|
||||||
for (let ruleIndex = 0; ruleIndex < style.sheet.cssRules.length; ruleIndex++)
|
|
||||||
{
|
|
||||||
let rule = style.sheet.cssRules.item(ruleIndex);
|
|
||||||
if(rule.selectorText == selector)
|
|
||||||
{
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** @returns {CSSRule} */
|
|
||||||
static removeRule(selector){
|
|
||||||
for (let ruleIndex = 0; ruleIndex < style.sheet.cssRules.length; ruleIndex++)
|
|
||||||
{
|
|
||||||
let rule = style.sheet.cssRules.item(ruleIndex);
|
|
||||||
if(rule.selectorText == selector)
|
|
||||||
{
|
|
||||||
style.sheet.deleteRule(ruleIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static css(selector, values){
|
|
||||||
let rule;
|
|
||||||
if(!(rule = CSSOM.findRule(selector)))
|
|
||||||
{
|
|
||||||
let no = style.sheet.insertRule(`${selector}{}`);
|
|
||||||
rule = style.sheet.cssRules[no];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let [stylename, stylevalue] of Object.entries(values))
|
|
||||||
{
|
|
||||||
let name = stylename;
|
|
||||||
let value = typeof stylevalue == "string" ? stylevalue.split('!')[0] : stylevalue;
|
|
||||||
let priority = typeof stylevalue == "string" ? (stylevalue.split('!')[1] == "important" ? "important" : void 0) : void 0;
|
|
||||||
|
|
||||||
|
|
||||||
name = name.replace(/([A-Z])/g,(_,$1) => '-' + $1.toLowerCase());
|
|
||||||
|
|
||||||
rule.style.setProperty(name, value, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static generate(idd = null, css = null){
|
|
||||||
let id = idd || CSSOM.generateCode();
|
|
||||||
let t = new CSSOM(id);
|
|
||||||
CSSOM.rules.set(t.id,t);
|
|
||||||
css && t.css(css);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
static generateCode(){
|
|
||||||
let mine;
|
|
||||||
do{
|
|
||||||
mine = Math.random().toString(36).slice(2,6);
|
|
||||||
}while(CSSOM.rules.has(mine));
|
|
||||||
return "i" + mine;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = "";
|
|
||||||
/** @type {{[key:string]: CSSRuleList}} */
|
|
||||||
rule_t = {};
|
|
||||||
constructor(id){
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
toString(){
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
css(name, value, prefix = 'owner'){
|
|
||||||
let rule;
|
|
||||||
if(!this.rule_t[prefix])
|
|
||||||
{
|
|
||||||
let rulename = `.${this.id}` + (prefix == "owner" ? "" : prefix);
|
|
||||||
let no = style.sheet.insertRule(`${rulename}{}`);
|
|
||||||
this.rule_t[prefix] = style.sheet.cssRules[no];
|
|
||||||
};
|
|
||||||
rule = this.rule_t[prefix];
|
|
||||||
if(value === undefined && typeof name == "string")
|
|
||||||
{
|
|
||||||
return rule.style.getPropertyValue(name);
|
|
||||||
}
|
|
||||||
if(value === null)
|
|
||||||
{
|
|
||||||
rule.style.removeProperty(name, value);
|
|
||||||
}else{
|
|
||||||
if(typeof name == "object")
|
|
||||||
{
|
|
||||||
for(let [stylename, stylevalue] of Object.entries(name))
|
|
||||||
{
|
|
||||||
let name = stylename;
|
|
||||||
let value = typeof stylevalue == "string" ? stylevalue.split('!')[0] : stylevalue;
|
|
||||||
let priority = typeof stylevalue == "string" ? (stylevalue.split('!')[1] == "important" ? "!important" : void 0) : stylevalue;
|
|
||||||
if(name.indexOf('-') == -1)
|
|
||||||
{
|
|
||||||
rule.style[name] = value + (priority || "");
|
|
||||||
}else{
|
|
||||||
rule.style.setProperty(name, value, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
rule.style.setProperty(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$.fn.cssom = function(obj, prefix){
|
|
||||||
if(prefix === void 0 || (typeof obj == "object" && typeof prefix == "string"))
|
|
||||||
{
|
|
||||||
let cssom;
|
|
||||||
if(this.attr("data-cssom"))
|
|
||||||
{
|
|
||||||
let name = this.attr("data-cssom");
|
|
||||||
cssom = CSSOM.get(name)
|
|
||||||
}else{
|
|
||||||
cssom = CSSOM.generate();
|
|
||||||
this.addClass(cssom.id);
|
|
||||||
this.attr("data-cssom", cssom.id);
|
|
||||||
}
|
|
||||||
cssom.css(obj,void 0,prefix);
|
|
||||||
}else{
|
|
||||||
|
|
||||||
if(obj.indexOf(":") == -1){
|
|
||||||
this.addClass(obj);
|
|
||||||
CSSOM.css("." + obj, prefix);
|
|
||||||
}else{
|
|
||||||
|
|
||||||
let [_class,_psudio] = obj.split(":");
|
|
||||||
|
|
||||||
this.addClass(_class);
|
|
||||||
CSSOM.css("." + _class + ":" + _psudio, prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.debugCssom = function(){
|
|
||||||
for (let ruleIndex = 0; ruleIndex < style.sheet.cssRules.length; ruleIndex++)
|
|
||||||
{
|
|
||||||
let rule = style.sheet.cssRules.item(ruleIndex);
|
|
||||||
console.log(rule.cssText)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.cssom = CSSOM;
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
* @ typedef {Object} BirthData
|
|
||||||
* @ property {Date} birthDate
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @typedef {Object} BrainCell
|
|
||||||
* @property {string} type
|
|
||||||
* @property {Object} data
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @type {BrainCell[]}
|
|
||||||
*/
|
|
||||||
export let memory = [];
|
|
||||||
|
|
||||||
export function AddCell(name, data)
|
|
||||||
{
|
|
||||||
memory.push({
|
|
||||||
type: name,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* @param {string} type
|
|
||||||
* @param {(BrainCell) => boolean} filterQuery
|
|
||||||
*/
|
|
||||||
export function RemoveCell(type, filterQuery)
|
|
||||||
{
|
|
||||||
memory = memory.filter(e => !(
|
|
||||||
e.type == type && filterQuery(e.data)
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} type
|
|
||||||
* @param {(BrainCell) => boolean} filterQuery
|
|
||||||
*/
|
|
||||||
export function FilterCell(type, filterQuery)
|
|
||||||
{
|
|
||||||
return memory.filter(e =>
|
|
||||||
e.type == type && filterQuery(e.data)
|
|
||||||
).map(e => e.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.memoryDump = () => memory;
|
|
||||||
|
|
@ -1,293 +0,0 @@
|
||||||
export default class DWindow extends EventTarget {
|
|
||||||
static allWindows = []; // açık pencereler
|
|
||||||
static zCounter = 1; // global sayaç
|
|
||||||
static normalizeThreshold = 9999; // yeniden sıfırlama eşiği
|
|
||||||
/** @type {JQuery<HTMLElement>} */ panel = null;
|
|
||||||
/** @type {JQuery<HTMLElement>} */ title = null;
|
|
||||||
/** @type {JQuery<HTMLElement>} */ content = null;
|
|
||||||
/** @type {JQuery<HTMLElement>} */ headtext = null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.initpanel();
|
|
||||||
this.enableFocus();
|
|
||||||
DWindow.allWindows.push(this);
|
|
||||||
this.bringToFront();
|
|
||||||
requestAnimationFrame(()=>{
|
|
||||||
this.enableDragging();
|
|
||||||
this.enableResize();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
activeDragging = true;
|
|
||||||
activeResizing = true;
|
|
||||||
|
|
||||||
initpanel() {
|
|
||||||
this.panel = $(`<div></div>`).css({
|
|
||||||
width: 450,
|
|
||||||
//height: 500,
|
|
||||||
position: "fixed",
|
|
||||||
left: (Math.random() * 300 | 0) + 100,
|
|
||||||
top: (Math.random() * 100 | 0) + 100,
|
|
||||||
boxShadow: "black 0px 0px 20px -10px",
|
|
||||||
borderRadius: "10px",
|
|
||||||
backgroundColor: "#3333335b",
|
|
||||||
backdropFilter: "blur(3px)",
|
|
||||||
border: "solid 1px #a2a2a2",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
outlineOffset: "-5px",
|
|
||||||
//overflow: "hidden",
|
|
||||||
userSelect: "none",
|
|
||||||
minWidth: "max-content",
|
|
||||||
minHeight: "max-content"
|
|
||||||
});
|
|
||||||
$("body").append(this.panel);
|
|
||||||
|
|
||||||
this.title = $(`<div></div>`).css({
|
|
||||||
display: "flex",
|
|
||||||
flex: "0 0 auto",
|
|
||||||
flexDirection: "row",
|
|
||||||
lineHeight: "2em",
|
|
||||||
backgroundColor: "#333333",
|
|
||||||
color: "white",
|
|
||||||
paddingLeft: "10px",
|
|
||||||
cursor: "move",
|
|
||||||
borderTopLeftRadius: "10px",
|
|
||||||
borderTopRightRadius: "10px",
|
|
||||||
});
|
|
||||||
|
|
||||||
this.content = $(`<div></div>`).css({
|
|
||||||
flex: "1 1 auto",
|
|
||||||
overflow: "auto",
|
|
||||||
borderBottomRightRadius: "10px",
|
|
||||||
borderBottomLeftRadius: "10px",
|
|
||||||
padding: "20px"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.panel.append(this.title, this.content);
|
|
||||||
this.initTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
initTitle() {
|
|
||||||
this.headtext = $("<div>Başlıksız pencere</div>");
|
|
||||||
const divider = $("<div style='flex:1 1 auto'></div>");
|
|
||||||
this.exitbtn = $("<div><span class='btn btn-sm material-symbols-outlined text-white'>close</span></div>");
|
|
||||||
this.exitbtn.on('click',()=>{
|
|
||||||
this.dispatchEvent(new Event("close"));
|
|
||||||
})
|
|
||||||
this.title.append(
|
|
||||||
this.headtext,
|
|
||||||
divider,
|
|
||||||
this.exitbtn
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
enableDragging() {
|
|
||||||
let isDragging = false;
|
|
||||||
let startX, startY, startLeft, startTop;
|
|
||||||
|
|
||||||
this.title.on("mousedown", (e) => {
|
|
||||||
if(this.activeDragging == false){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let isInput = $(e.target).closest("button, input, select, textarea,.btn");
|
|
||||||
if (isInput.length) return;
|
|
||||||
e.preventDefault();
|
|
||||||
isDragging = true;
|
|
||||||
startX = e.pageX;
|
|
||||||
startY = e.pageY;
|
|
||||||
startLeft = parseFloat(this.panel.css("left"));
|
|
||||||
startTop = parseFloat(this.panel.css("top"));
|
|
||||||
$("body").css("user-select", "none");
|
|
||||||
this.dispatchEvent(new Event("dragging"));
|
|
||||||
this.panel.css("box-shadow","black 0px 0px 40px -20px");
|
|
||||||
this.panel.css("backdrop-filter","blur(10px)");
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mousemove", (e) => {
|
|
||||||
if (!isDragging) return;
|
|
||||||
const dx = e.pageX - startX;
|
|
||||||
const dy = e.pageY - startY;
|
|
||||||
|
|
||||||
this.panel.css({
|
|
||||||
left: startLeft + dx,
|
|
||||||
top: startTop + dy,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mouseup", () => {
|
|
||||||
if (isDragging) {
|
|
||||||
isDragging = false;
|
|
||||||
this.panel.css("box-shadow","black 0px 0px 20px -10px");
|
|
||||||
this.panel.css("backdrop-filter","blur(3px)");
|
|
||||||
$("body").css("user-select", "auto");
|
|
||||||
this.dispatchEvent(new Event("draggingend"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
enableResize() {
|
|
||||||
const offset = 5; // panelin dışından 5px alan
|
|
||||||
let resizing = false;
|
|
||||||
let resizeDir = "";
|
|
||||||
let startX, startY, startW, startH, startL, startT;
|
|
||||||
const minW = 200, minH = 150;
|
|
||||||
|
|
||||||
// overlay divleri oluştur
|
|
||||||
const handles = {};
|
|
||||||
const names = ["n","s","e","w","ne","nw","se","sw"];
|
|
||||||
names.forEach(n => {
|
|
||||||
handles[n] = $('<div></div>').css({
|
|
||||||
position: "absolute",
|
|
||||||
zIndex: 1000,
|
|
||||||
background: "transparent",
|
|
||||||
cursor: n + "-resize",
|
|
||||||
transition: "all 0.5s all",
|
|
||||||
outlineOffset: "3px"
|
|
||||||
}).appendTo(this.panel);
|
|
||||||
});
|
|
||||||
|
|
||||||
// boyut ve pozisyonlarını ayarla
|
|
||||||
const updateHandles = () => {
|
|
||||||
const w = this.panel.outerWidth();
|
|
||||||
const h = this.panel.outerHeight();
|
|
||||||
// kenarlar
|
|
||||||
handles.n.css({ top: -offset, left: 0, width: w, height: offset*2 });
|
|
||||||
handles.s.css({ bottom: -offset, left: 0, width: w, height: offset*2 });
|
|
||||||
handles.w.css({ top: 0, left: -offset, width: offset*2, height: h });
|
|
||||||
handles.e.css({ top: 0, right: -offset, width: offset*2, height: h });
|
|
||||||
// köşeler
|
|
||||||
handles.nw.css({ top: -offset, left: -offset, width: offset*2, height: offset*2 });
|
|
||||||
handles.ne.css({ top: -offset, right: -offset, width: offset*2, height: offset*2 });
|
|
||||||
handles.sw.css({ bottom: -offset, left: -offset, width: offset*2, height: offset*2 });
|
|
||||||
handles.se.css({ bottom: -offset, right: -offset, width: offset*2, height: offset*2 });
|
|
||||||
};
|
|
||||||
updateHandles();
|
|
||||||
|
|
||||||
$(window).on("resize", updateHandles);
|
|
||||||
|
|
||||||
let activeDir = null;
|
|
||||||
|
|
||||||
// resize eventleri
|
|
||||||
names.forEach(dir => {
|
|
||||||
handles[dir].on("mousedown", (e) => {
|
|
||||||
if(this.activeResizing == false){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
activeDir = e.target;
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
resizing = true;
|
|
||||||
resizeDir = dir;
|
|
||||||
startX = e.pageX;
|
|
||||||
startY = e.pageY;
|
|
||||||
startW = this.panel.outerWidth();
|
|
||||||
startH = this.panel.outerHeight();
|
|
||||||
startL = parseFloat(this.panel.css("left"));
|
|
||||||
startT = parseFloat(this.panel.css("top"));
|
|
||||||
this.dispatchEvent(new Event("resizing"));
|
|
||||||
$("body").css("user-select","none");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mousemove", (e) => {
|
|
||||||
if (!resizing) return;
|
|
||||||
const dx = e.pageX - startX;
|
|
||||||
const dy = e.pageY - startY;
|
|
||||||
let newW = startW;
|
|
||||||
let newH = startH;
|
|
||||||
let newL = startL;
|
|
||||||
let newT = startT;
|
|
||||||
|
|
||||||
if (resizeDir.includes("e")) newW = Math.max(minW, startW + dx);
|
|
||||||
if (resizeDir.includes("w")) { newW = Math.max(minW, startW - dx); newL = startL + (startW - newW); }
|
|
||||||
if (resizeDir.includes("s")) newH = Math.max(minH, startH + dy);
|
|
||||||
if (resizeDir.includes("n")) { newH = Math.max(minH, startH - dy); newT = startT + (startH - newH); }
|
|
||||||
|
|
||||||
this.panel.css({ width: newW, height: newH, left: newL, top: newT });
|
|
||||||
updateHandles();
|
|
||||||
$(activeDir).css("outline", "solid 2px #00ff00");
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("mouseup", () => {
|
|
||||||
if (resizing) {
|
|
||||||
resizing = false;
|
|
||||||
$("body").css("user-select","auto");
|
|
||||||
$(activeDir).css("outline", "solid 0px #00ff00");
|
|
||||||
this.dispatchEvent(new Event("resizingend"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
enableFocus() {
|
|
||||||
this.panel.on("mousedown", () => {
|
|
||||||
this.bringToFront();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bringToFront() {
|
|
||||||
if (DWindow.zCounter > DWindow.normalizeThreshold) {
|
|
||||||
DWindow.normalizeZ();
|
|
||||||
}
|
|
||||||
DWindow.zCounter++;
|
|
||||||
this.panel.css("z-index", DWindow.zCounter);
|
|
||||||
}
|
|
||||||
showing = true;
|
|
||||||
show(){
|
|
||||||
this.dispatchEvent(new Event("show"));
|
|
||||||
this.panel.show();
|
|
||||||
this.showing = true;
|
|
||||||
}
|
|
||||||
hide(){
|
|
||||||
this.dispatchEvent(new Event("hide"));
|
|
||||||
this.panel.hide();
|
|
||||||
this.showing = false;
|
|
||||||
}
|
|
||||||
toggle(){
|
|
||||||
this.showing ? this.hide() : this.show();
|
|
||||||
}
|
|
||||||
exit(){
|
|
||||||
this.dispatchEvent(new Event("exit"));
|
|
||||||
this.panel.remove();
|
|
||||||
}
|
|
||||||
/** @type {DWindow} */
|
|
||||||
parent = null;
|
|
||||||
/** @param {DWindow} parent */
|
|
||||||
enableDialog(parent){
|
|
||||||
this.parent = parent;
|
|
||||||
this.parent.activeDragging = false;
|
|
||||||
this.parent.activeResizing = false;
|
|
||||||
this.parent.panel.css('pointer-events',"none");
|
|
||||||
this.parent.panel.css('filter',"grayscale(1)");
|
|
||||||
|
|
||||||
let [{
|
|
||||||
left: parentL,
|
|
||||||
top: parentT,
|
|
||||||
width: parentW,
|
|
||||||
height: parentH
|
|
||||||
}] = this.parent.panel[0].getClientRects();
|
|
||||||
|
|
||||||
let [{
|
|
||||||
width: childW,
|
|
||||||
height: childH
|
|
||||||
}] = this.panel[0].getClientRects();
|
|
||||||
|
|
||||||
let newLeft = parentL + (parentW - childW) / 2;
|
|
||||||
let newTop = parentT + (parentH - childH) / 2;
|
|
||||||
debugger;
|
|
||||||
newLeft = Math.max(newLeft, 20);
|
|
||||||
newTop = Math.max(newTop, 20);
|
|
||||||
newLeft = Math.min(newLeft, screen.availWidth - 100);
|
|
||||||
newTop = Math.min(newTop, screen.availHeight - 100);
|
|
||||||
|
|
||||||
Object.assign(this.panel[0].style, {
|
|
||||||
left: `${Math.round(newLeft)}px`,
|
|
||||||
top: `${Math.round(newTop)}px`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/** @param {DWindow} parent */
|
|
||||||
disableDialog(){
|
|
||||||
this.parent.activeDragging = true;
|
|
||||||
this.parent.activeResizing = true;
|
|
||||||
this.parent.panel.css('pointer-events',"all");
|
|
||||||
this.parent.panel.css('filter',"initial");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
let mwse = new MWSE({
|
|
||||||
endpoint: "ws://localhost:7707"
|
|
||||||
});
|
|
||||||
|
|
||||||
mwse.scope(beginEngine);
|
|
||||||
|
|
||||||
async function beginEngine()
|
|
||||||
{
|
|
||||||
room = mwse.room({
|
|
||||||
name: "Naber",
|
|
||||||
joinType: "free",
|
|
||||||
accessType: "private",
|
|
||||||
description: "Private free joined room",
|
|
||||||
ifexistsJoin: false,
|
|
||||||
notifyActionEjected: false,
|
|
||||||
notifyActionInvite: false,
|
|
||||||
notifyActionJoined: false
|
|
||||||
});
|
|
||||||
try{
|
|
||||||
await room.createRoom();
|
|
||||||
let time = 0;
|
|
||||||
setInterval(()=>{
|
|
||||||
room.info.set("set time",time);
|
|
||||||
time++;
|
|
||||||
},1000);
|
|
||||||
}catch{
|
|
||||||
await room.join();
|
|
||||||
}
|
|
||||||
room.on('updateinfo',(name, value) => {
|
|
||||||
console.log("read",name,value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Dosya Transfer Sistem</title>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/css/bootstrap.min.css">
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="https://ws.saqut.com/script"></script>
|
|
||||||
<script src="m.h.2.8.8.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="tr">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<title>jQuery ile Resizable Kutu</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, Helvetica, sans-serif; height:100vh; margin:0; display:flex; align-items:center; justify-content:center; background:#f2f2f2; }
|
|
||||||
.stage { width:90vw; height:80vh; border:1px solid #ccc; position:relative; background:#fff; }
|
|
||||||
.box {
|
|
||||||
position:absolute;
|
|
||||||
left:50px; top:50px;
|
|
||||||
width:300px; height:200px;
|
|
||||||
background:#e8f0ff;
|
|
||||||
border:1px solid #5b8cff;
|
|
||||||
box-sizing:border-box;
|
|
||||||
user-select:none;
|
|
||||||
}
|
|
||||||
/* küçücük tutamaklar (handles) */
|
|
||||||
.handle {
|
|
||||||
position:absolute;
|
|
||||||
width:12px; height:12px;
|
|
||||||
background:#5b8cff;
|
|
||||||
border-radius:2px;
|
|
||||||
margin:-6px 0 0 -6px; /* merkezler */
|
|
||||||
cursor:default;
|
|
||||||
}
|
|
||||||
.handle.n { top:0; left:50%; cursor:n-resize; }
|
|
||||||
.handle.s { bottom:0; left:50%; cursor:s-resize; }
|
|
||||||
.handle.e { right:0; top:50%; cursor:e-resize; }
|
|
||||||
.handle.w { left:0; top:50%; cursor:w-resize; }
|
|
||||||
.handle.ne { right:0; top:0; cursor:ne-resize; }
|
|
||||||
.handle.nw { left:0; top:0; cursor:nw-resize; }
|
|
||||||
.handle.se { right:0; bottom:0; cursor:se-resize; }
|
|
||||||
.handle.sw { left:0; bottom:0; cursor:sw-resize; }
|
|
||||||
/* bilgi satırı (isteğe bağlı) */
|
|
||||||
.info { position:absolute; left:6px; bottom:6px; font-size:12px; color:#333; background:rgba(255,255,255,0.8); padding:4px 6px; border-radius:4px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="stage">
|
|
||||||
<div class="box" id="resizable">
|
|
||||||
<div class="handle n" data-dir="n"></div>
|
|
||||||
<div class="handle s" data-dir="s"></div>
|
|
||||||
<div class="handle e" data-dir="e"></div>
|
|
||||||
<div class="handle w" data-dir="w"></div>
|
|
||||||
<div class="handle ne" data-dir="ne"></div>
|
|
||||||
<div class="handle nw" data-dir="nw"></div>
|
|
||||||
<div class="handle se" data-dir="se"></div>
|
|
||||||
<div class="handle sw" data-dir="sw"></div>
|
|
||||||
<div class="info" id="info"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
||||||
<script>
|
|
||||||
$(function(){
|
|
||||||
var $doc = $(document);
|
|
||||||
var $box = $('#resizable');
|
|
||||||
var dragging = false;
|
|
||||||
var dir = null;
|
|
||||||
var start = {}; // {mouseX, mouseY, left, top, width, height}
|
|
||||||
var minW = 60, minH = 40;
|
|
||||||
|
|
||||||
function updateInfo(){
|
|
||||||
var w = Math.round($box.width()), h = Math.round($box.height());
|
|
||||||
$('#info').text(w + ' x ' + h);
|
|
||||||
}
|
|
||||||
updateInfo();
|
|
||||||
|
|
||||||
$box.on('mousedown', '.handle', function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
dragging = true;
|
|
||||||
dir = $(this).data('dir');
|
|
||||||
var offset = $box.position();
|
|
||||||
start = {
|
|
||||||
mouseX: e.pageX,
|
|
||||||
mouseY: e.pageY,
|
|
||||||
left: offset.left,
|
|
||||||
top: offset.top,
|
|
||||||
width: $box.outerWidth(),
|
|
||||||
height: $box.outerHeight()
|
|
||||||
};
|
|
||||||
// disable text selection while dragging
|
|
||||||
$('body').css('user-select','none');
|
|
||||||
});
|
|
||||||
|
|
||||||
$doc.on('mousemove', function(e){
|
|
||||||
if(!dragging) return;
|
|
||||||
e.preventDefault();
|
|
||||||
var dx = e.pageX - start.mouseX;
|
|
||||||
var dy = e.pageY - start.mouseY;
|
|
||||||
var newLeft = start.left;
|
|
||||||
var newTop = start.top;
|
|
||||||
var newW = start.width;
|
|
||||||
var newH = start.height;
|
|
||||||
|
|
||||||
// handle horizontal changes
|
|
||||||
if(dir.indexOf('e') !== -1){
|
|
||||||
newW = Math.max(minW, start.width + dx);
|
|
||||||
}
|
|
||||||
if(dir.indexOf('w') !== -1){
|
|
||||||
newW = Math.max(minW, start.width - dx);
|
|
||||||
newLeft = start.left + (start.width - newW);
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle vertical changes
|
|
||||||
if(dir.indexOf('s') !== -1){
|
|
||||||
newH = Math.max(minH, start.height + dy);
|
|
||||||
}
|
|
||||||
if(dir.indexOf('n') !== -1){
|
|
||||||
newH = Math.max(minH, start.height - dy);
|
|
||||||
newTop = start.top + (start.height - newH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply
|
|
||||||
$box.css({ left: newLeft + 'px', top: newTop + 'px', width: newW + 'px', height: newH + 'px' });
|
|
||||||
updateInfo();
|
|
||||||
});
|
|
||||||
|
|
||||||
$doc.on('mouseup mouseleave', function(){
|
|
||||||
if(dragging){
|
|
||||||
dragging = false;
|
|
||||||
dir = null;
|
|
||||||
$('body').css('user-select','auto');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// optional: allow dragging the whole box when clicking inside (but not on handles)
|
|
||||||
var draggingBox = false, dragBoxStart = {};
|
|
||||||
$box.on('mousedown', function(e){
|
|
||||||
if($(e.target).hasClass('handle')) return;
|
|
||||||
e.preventDefault();
|
|
||||||
draggingBox = true;
|
|
||||||
dragBoxStart = { mouseX: e.pageX, mouseY: e.pageY, left: $box.position().left, top: $box.position().top };
|
|
||||||
$('body').css('user-select','none');
|
|
||||||
});
|
|
||||||
$doc.on('mousemove', function(e){
|
|
||||||
if(!draggingBox) return;
|
|
||||||
var nx = dragBoxStart.left + (e.pageX - dragBoxStart.mouseX);
|
|
||||||
var ny = dragBoxStart.top + (e.pageY - dragBoxStart.mouseY);
|
|
||||||
$box.css({ left: nx + 'px', top: ny + 'px' });
|
|
||||||
});
|
|
||||||
$doc.on('mouseup', function(){
|
|
||||||
if(draggingBox){
|
|
||||||
draggingBox = false;
|
|
||||||
$('body').css('user-select','auto');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// keyboard nudges (isteğe bağlı)
|
|
||||||
$doc.on('keydown', function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
if(!e.altKey) return; // alt ile etkinleştir
|
|
||||||
var pos = $box.position();
|
|
||||||
if(e.key === 'ArrowLeft') $box.css('left', pos.left - 1);
|
|
||||||
if(e.key === 'ArrowRight') $box.css('left', pos.left + 1);
|
|
||||||
if(e.key === 'ArrowUp') $box.css('top', pos.top - 1);
|
|
||||||
if(e.key === 'ArrowDown') $box.css('top', pos.top + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,714 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="tr">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Network meter</title>
|
|
||||||
</head>
|
|
||||||
<body style="background-color: #333333;">
|
|
||||||
<div id="container">
|
|
||||||
<div class="speed-container">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
html,body,#container{
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
#container{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.speed-container{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
max-width: 1200px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.speed-container > div{
|
|
||||||
flex: 1 1 25%;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
margin-bottom: 50px;
|
|
||||||
}
|
|
||||||
.speed-container > div canvas{
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.speed-container > div .text{
|
|
||||||
display: flex;
|
|
||||||
color: white;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.speed-container > div .mwse{
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
}
|
|
||||||
.speed-container > div .text > .text1{
|
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
font-size: 2em;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.speed-container > div .text > .text2{
|
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
||||||
font-size: 2em;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.speed-container > div .text1::after{
|
|
||||||
content:' FLOW';
|
|
||||||
font-size: 0.4em;
|
|
||||||
margin: auto 0 auto 5px;
|
|
||||||
}
|
|
||||||
.speed-container > div .text2::after{
|
|
||||||
content:' PRESSURE';
|
|
||||||
font-size: 0.4em;
|
|
||||||
margin: auto 0 auto 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
function CreateMeter()
|
|
||||||
{
|
|
||||||
var iCurrentSpeed = 0,
|
|
||||||
iTargetSpeed = 0,
|
|
||||||
bDecrement = null,
|
|
||||||
job = null;
|
|
||||||
|
|
||||||
function degToRad(angle) {
|
|
||||||
// Degrees to radians
|
|
||||||
return ((angle * Math.PI) / 180);
|
|
||||||
}
|
|
||||||
|
|
||||||
function radToDeg(angle) {
|
|
||||||
// Radians to degree
|
|
||||||
return ((angle * 180) / Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawLine(options, line) {
|
|
||||||
// Draw a line using the line object passed in
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
// Set attributes of open
|
|
||||||
options.ctx.globalAlpha = line.alpha;
|
|
||||||
options.ctx.lineWidth = line.lineWidth;
|
|
||||||
options.ctx.fillStyle = line.fillStyle;
|
|
||||||
options.ctx.strokeStyle = line.fillStyle;
|
|
||||||
options.ctx.moveTo(line.from.X,
|
|
||||||
line.from.Y);
|
|
||||||
|
|
||||||
// Plot the line
|
|
||||||
options.ctx.lineTo(
|
|
||||||
line.to.X,
|
|
||||||
line.to.Y
|
|
||||||
);
|
|
||||||
|
|
||||||
options.ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLine(fromX, fromY, toX, toY, fillStyle, lineWidth, alpha) {
|
|
||||||
// Create a line object using Javascript object notation
|
|
||||||
return {
|
|
||||||
from: {
|
|
||||||
X: fromX,
|
|
||||||
Y: fromY
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
X: toX,
|
|
||||||
Y: toY
|
|
||||||
},
|
|
||||||
fillStyle: fillStyle,
|
|
||||||
lineWidth: lineWidth,
|
|
||||||
alpha: alpha
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawOuterMetallicArc(options) {
|
|
||||||
/* Draw the metallic border of the speedometer
|
|
||||||
* Outer grey area
|
|
||||||
*/
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
// Nice shade of grey
|
|
||||||
options.ctx.fillStyle = "rgb(127,127,127)";
|
|
||||||
|
|
||||||
// Draw the outer circle
|
|
||||||
options.ctx.arc(options.center.X,
|
|
||||||
options.center.Y,
|
|
||||||
options.radius,
|
|
||||||
0,
|
|
||||||
Math.PI,
|
|
||||||
true);
|
|
||||||
|
|
||||||
// Fill the last object
|
|
||||||
options.ctx.fill();
|
|
||||||
/* */
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawInnerMetallicArc(options) {
|
|
||||||
/* Draw the metallic border of the speedometer
|
|
||||||
* Inner white area
|
|
||||||
*/
|
|
||||||
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
// White
|
|
||||||
options.ctx.fillStyle = "rgb(255,255,255)";
|
|
||||||
|
|
||||||
// Outer circle (subtle edge in the grey)
|
|
||||||
options.ctx.arc(options.center.X,
|
|
||||||
options.center.Y,
|
|
||||||
(options.radius / 100) * 90,
|
|
||||||
0,
|
|
||||||
Math.PI,
|
|
||||||
true);
|
|
||||||
|
|
||||||
options.ctx.fill();
|
|
||||||
|
|
||||||
/* */
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawMetallicArc(options) {
|
|
||||||
/* Draw the metallic border of the speedometer
|
|
||||||
* by drawing two semi-circles, one over lapping
|
|
||||||
* the other with a bot of alpha transparency
|
|
||||||
*/
|
|
||||||
|
|
||||||
drawOuterMetallicArc(options);
|
|
||||||
drawInnerMetallicArc(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBackground(options) {
|
|
||||||
/* Black background with alphs transparency to
|
|
||||||
* blend the edges of the metallic edge and
|
|
||||||
* black background
|
|
||||||
*/
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
options.ctx.globalAlpha = 0.2;
|
|
||||||
options.ctx.fillStyle = "rgb(0,0,0)";
|
|
||||||
|
|
||||||
// Draw semi-transparent circles
|
|
||||||
for (i = 170; i < 180; i++) {
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
options.ctx.arc(options.center.X,
|
|
||||||
options.center.Y,
|
|
||||||
i,
|
|
||||||
0,
|
|
||||||
Math.PI,
|
|
||||||
true);
|
|
||||||
|
|
||||||
options.ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyDefaultContextSettings(options) {
|
|
||||||
/* Helper function to revert to gauges
|
|
||||||
* default settings
|
|
||||||
*/
|
|
||||||
|
|
||||||
options.ctx.lineWidth = 2;
|
|
||||||
options.ctx.globalAlpha = 0.5;
|
|
||||||
options.ctx.strokeStyle = "rgb(255, 255, 255)";
|
|
||||||
options.ctx.fillStyle = 'rgb(255,255,255)';
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSmallTickMarks(options) {
|
|
||||||
/* The small tick marks against the coloured
|
|
||||||
* arc drawn every 5 mph from 10 degrees to
|
|
||||||
* 170 degrees.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var tickvalue = options.levelRadius - 8,
|
|
||||||
iTick = 0,
|
|
||||||
gaugeOptions = options.gaugeOptions,
|
|
||||||
iTickRad = 0,
|
|
||||||
onArchX,
|
|
||||||
onArchY,
|
|
||||||
innerTickX,
|
|
||||||
innerTickY,
|
|
||||||
fromX,
|
|
||||||
fromY,
|
|
||||||
line,
|
|
||||||
toX,
|
|
||||||
toY;
|
|
||||||
|
|
||||||
applyDefaultContextSettings(options);
|
|
||||||
|
|
||||||
// Tick every 20 degrees (small ticks)
|
|
||||||
for (iTick = 10; iTick < 180; iTick += 10) {
|
|
||||||
|
|
||||||
iTickRad = degToRad(iTick);
|
|
||||||
|
|
||||||
/* Calculate the X and Y of both ends of the
|
|
||||||
* line I need to draw at angle represented at Tick.
|
|
||||||
* The aim is to draw the a line starting on the
|
|
||||||
* coloured arc and continueing towards the outer edge
|
|
||||||
* in the direction from the center of the gauge.
|
|
||||||
*/
|
|
||||||
|
|
||||||
onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
|
|
||||||
onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
|
|
||||||
innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
|
|
||||||
innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);
|
|
||||||
|
|
||||||
fromX = (options.center.X - gaugeOptions.radius) + onArchX;
|
|
||||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
|
|
||||||
toX = (options.center.X - gaugeOptions.radius) + innerTickX;
|
|
||||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;
|
|
||||||
|
|
||||||
// Create a line expressed in JSON
|
|
||||||
line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);
|
|
||||||
|
|
||||||
// Draw the line
|
|
||||||
drawLine(options, line);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawLargeTickMarks(options) {
|
|
||||||
/* The large tick marks against the coloured
|
|
||||||
* arc drawn every 10 mph from 10 degrees to
|
|
||||||
* 170 degrees.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var tickvalue = options.levelRadius - 8,
|
|
||||||
iTick = 0,
|
|
||||||
gaugeOptions = options.gaugeOptions,
|
|
||||||
iTickRad = 0,
|
|
||||||
innerTickY,
|
|
||||||
innerTickX,
|
|
||||||
onArchX,
|
|
||||||
onArchY,
|
|
||||||
fromX,
|
|
||||||
fromY,
|
|
||||||
toX,
|
|
||||||
toY,
|
|
||||||
line;
|
|
||||||
|
|
||||||
applyDefaultContextSettings(options);
|
|
||||||
|
|
||||||
tickvalue = options.levelRadius - 2;
|
|
||||||
|
|
||||||
// 10 units (major ticks)
|
|
||||||
for (iTick = 20; iTick < 180; iTick += 20) {
|
|
||||||
|
|
||||||
iTickRad = degToRad(iTick);
|
|
||||||
|
|
||||||
/* Calculate the X and Y of both ends of the
|
|
||||||
* line I need to draw at angle represented at Tick.
|
|
||||||
* The aim is to draw the a line starting on the
|
|
||||||
* coloured arc and continueing towards the outer edge
|
|
||||||
* in the direction from the center of the gauge.
|
|
||||||
*/
|
|
||||||
|
|
||||||
onArchX = gaugeOptions.radius - (Math.cos(iTickRad) * tickvalue);
|
|
||||||
onArchY = gaugeOptions.radius - (Math.sin(iTickRad) * tickvalue);
|
|
||||||
innerTickX = gaugeOptions.radius - (Math.cos(iTickRad) * gaugeOptions.radius);
|
|
||||||
innerTickY = gaugeOptions.radius - (Math.sin(iTickRad) * gaugeOptions.radius);
|
|
||||||
|
|
||||||
fromX = (options.center.X - gaugeOptions.radius) + onArchX;
|
|
||||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + onArchY;
|
|
||||||
toX = (options.center.X - gaugeOptions.radius) + innerTickX;
|
|
||||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY;
|
|
||||||
|
|
||||||
// Create a line expressed in JSON
|
|
||||||
line = createLine(fromX, fromY, toX, toY, "rgb(127,127,127)", 3, 0.6);
|
|
||||||
|
|
||||||
// Draw the line
|
|
||||||
drawLine(options, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawTicks(options) {
|
|
||||||
/* Two tick in the coloured arc!
|
|
||||||
* Small ticks every 5
|
|
||||||
* Large ticks every 10
|
|
||||||
*/
|
|
||||||
drawSmallTickMarks(options);
|
|
||||||
drawLargeTickMarks(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawTextMarkers(options) {
|
|
||||||
/* The text labels marks above the coloured
|
|
||||||
* arc drawn every 10 mph from 10 degrees to
|
|
||||||
* 170 degrees.
|
|
||||||
*/
|
|
||||||
var innerTickX = 0,
|
|
||||||
innerTickY = 0,
|
|
||||||
iTick = 0,
|
|
||||||
gaugeOptions = options.gaugeOptions,
|
|
||||||
iTickToPrint = 00;
|
|
||||||
|
|
||||||
applyDefaultContextSettings(options);
|
|
||||||
|
|
||||||
// Font styling
|
|
||||||
options.ctx.font = 'italic 10px sans-serif';
|
|
||||||
options.ctx.textBaseline = 'top';
|
|
||||||
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
// Tick every 20 (small ticks)
|
|
||||||
for (iTick = 10; iTick < 160; iTick += 20) {
|
|
||||||
|
|
||||||
innerTickX = gaugeOptions.radius - (Math.cos(degToRad(iTick)) * gaugeOptions.radius);
|
|
||||||
innerTickY = gaugeOptions.radius - (Math.sin(degToRad(iTick)) * gaugeOptions.radius);
|
|
||||||
|
|
||||||
// Some cludging to center the values (TODO: Improve)
|
|
||||||
if (iTick <= 10) {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
|
||||||
} else if (iTick < 50) {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX - 5,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
|
||||||
} else if (iTick < 90) {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
|
||||||
} else if (iTick === 90) {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 4,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
|
||||||
} else if (iTick < 145) {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 10,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY);
|
|
||||||
} else {
|
|
||||||
options.ctx.fillText(iTickToPrint, (options.center.X - gaugeOptions.radius - 12) + innerTickX + 15,
|
|
||||||
(gaugeOptions.center.Y - gaugeOptions.radius - 12) + innerTickY + 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MPH increase by 10 every 20 degrees
|
|
||||||
//iTickToPrint += Math.round(2160 / 9);
|
|
||||||
iTickToPrint += 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSpeedometerPart(options, alphaValue, strokeStyle, startPos) {
|
|
||||||
/* Draw part of the arc that represents
|
|
||||||
* the colour speedometer arc
|
|
||||||
*/
|
|
||||||
|
|
||||||
options.ctx.beginPath();
|
|
||||||
|
|
||||||
options.ctx.globalAlpha = alphaValue;
|
|
||||||
options.ctx.lineWidth = 5;
|
|
||||||
options.ctx.strokeStyle = strokeStyle;
|
|
||||||
|
|
||||||
options.ctx.arc(options.center.X,
|
|
||||||
options.center.Y,
|
|
||||||
options.levelRadius,
|
|
||||||
Math.PI + (Math.PI / 360 * startPos),
|
|
||||||
0 - (Math.PI / 360 * 10),
|
|
||||||
false);
|
|
||||||
|
|
||||||
options.ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSpeedometerColourArc(options) {
|
|
||||||
/* Draws the colour arc. Three different colours
|
|
||||||
* used here; thus, same arc drawn 3 times with
|
|
||||||
* different colours.
|
|
||||||
* TODO: Gradient possible?
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
drawSpeedometerPart(options, 1.0, "rgb(255,255,255)", 10);
|
|
||||||
drawSpeedometerPart(options, 0.9, "rgb(0,255,0)", 80);
|
|
||||||
drawSpeedometerPart(options, 0.9, "rgb(255,128,0)", 250);
|
|
||||||
drawSpeedometerPart(options, 0.9, "rgb(255,0,0)", 300);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawNeedleDial(options, alphaValue, strokeStyle, fillStyle) {
|
|
||||||
/* Draws the metallic dial that covers the base of the
|
|
||||||
* needle.
|
|
||||||
*/
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
options.ctx.globalAlpha = alphaValue;
|
|
||||||
options.ctx.lineWidth = 3;
|
|
||||||
options.ctx.strokeStyle = strokeStyle;
|
|
||||||
options.ctx.fillStyle = fillStyle;
|
|
||||||
|
|
||||||
// Draw several transparent circles with alpha
|
|
||||||
for (i = 0; i < 30; i++) {
|
|
||||||
|
|
||||||
options.ctx.beginPath();
|
|
||||||
options.ctx.arc(options.center.X,
|
|
||||||
options.center.Y,
|
|
||||||
i,
|
|
||||||
0,
|
|
||||||
Math.PI,
|
|
||||||
true);
|
|
||||||
|
|
||||||
options.ctx.fill();
|
|
||||||
options.ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function drawNeedle(options) {
|
|
||||||
/* Draw the needle in a nice read colour at the
|
|
||||||
* angle that represents the options.speed value.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var iSpeedAsAngle = convertSpeedToAngle(options),
|
|
||||||
iSpeedAsAngleRad = degToRad(iSpeedAsAngle),
|
|
||||||
gaugeOptions = options.gaugeOptions,
|
|
||||||
innerTickX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * 20),
|
|
||||||
innerTickY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * 20),
|
|
||||||
fromX = (options.center.X - gaugeOptions.radius) + innerTickX,
|
|
||||||
fromY = (gaugeOptions.center.Y - gaugeOptions.radius) + innerTickY,
|
|
||||||
endNeedleX = gaugeOptions.radius - (Math.cos(iSpeedAsAngleRad) * gaugeOptions.radius),
|
|
||||||
endNeedleY = gaugeOptions.radius - (Math.sin(iSpeedAsAngleRad) * gaugeOptions.radius),
|
|
||||||
toX = (options.center.X - gaugeOptions.radius) + endNeedleX,
|
|
||||||
toY = (gaugeOptions.center.Y - gaugeOptions.radius) + endNeedleY,
|
|
||||||
line = createLine(fromX, fromY, toX, toY, "rgb(255, 0, 0)", 5, 0.6);
|
|
||||||
|
|
||||||
drawLine(options, line);
|
|
||||||
|
|
||||||
// Two circle to draw the dial at the base (give its a nice effect?)
|
|
||||||
drawNeedleDial(options, 0.6, "rgb(127, 127, 127)", "rgb(255,255,255)");
|
|
||||||
drawNeedleDial(options, 0.2, "rgb(127, 127, 127)", "rgb(127,127,127)");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildOptionsAsJSON(canvas, iSpeed) {
|
|
||||||
/* Setting for the speedometer
|
|
||||||
* Alter these to modify its look and feel
|
|
||||||
*/
|
|
||||||
|
|
||||||
var centerX = 210,
|
|
||||||
centerY = 210,
|
|
||||||
radius = 150,
|
|
||||||
outerRadius = 200;
|
|
||||||
|
|
||||||
// Create a speedometer object using Javascript object notation
|
|
||||||
return {
|
|
||||||
ctx: canvas.getContext('2d'),
|
|
||||||
speed: iSpeed,
|
|
||||||
center: {
|
|
||||||
X: centerX,
|
|
||||||
Y: centerY
|
|
||||||
},
|
|
||||||
levelRadius: radius - 10,
|
|
||||||
gaugeOptions: {
|
|
||||||
center: {
|
|
||||||
X: centerX,
|
|
||||||
Y: centerY
|
|
||||||
},
|
|
||||||
radius: radius
|
|
||||||
},
|
|
||||||
radius: outerRadius
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearCanvas(options) {
|
|
||||||
options.ctx.clearRect(0, 0, 800, 600);
|
|
||||||
applyDefaultContextSettings(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
/* Main entry point for drawing the speedometer
|
|
||||||
* If canvas is not support alert the user.
|
|
||||||
*/
|
|
||||||
var canvas = createCanvas.value,
|
|
||||||
options = null;
|
|
||||||
|
|
||||||
// Canvas good?
|
|
||||||
if (canvas !== null && canvas.getContext) {
|
|
||||||
options = buildOptionsAsJSON(canvas, iCurrentSpeed);
|
|
||||||
|
|
||||||
// Clear canvas
|
|
||||||
clearCanvas(options);
|
|
||||||
|
|
||||||
// Draw the metallic styled edge
|
|
||||||
drawMetallicArc(options);
|
|
||||||
|
|
||||||
// Draw thw background
|
|
||||||
drawBackground(options);
|
|
||||||
|
|
||||||
// Draw tick marks
|
|
||||||
drawTicks(options);
|
|
||||||
|
|
||||||
// Draw labels on markers
|
|
||||||
drawTextMarkers(options);
|
|
||||||
|
|
||||||
// Draw speeometer colour arc
|
|
||||||
drawSpeedometerColourArc(options);
|
|
||||||
|
|
||||||
// Draw the needle and base
|
|
||||||
drawNeedle(options);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
alert("Canvas not supported by your browser!");
|
|
||||||
};
|
|
||||||
|
|
||||||
if(iTargetSpeed == iCurrentSpeed) {
|
|
||||||
cancelAnimationFrame(job);
|
|
||||||
return;
|
|
||||||
} else if(iTargetSpeed < iCurrentSpeed) {
|
|
||||||
bDecrement = true;
|
|
||||||
} else if(iTargetSpeed > iCurrentSpeed) {
|
|
||||||
bDecrement = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(iTargetSpeed,bDecrement)
|
|
||||||
|
|
||||||
if(bDecrement) {
|
|
||||||
iCurrentSpeed = iCurrentSpeed - 1;
|
|
||||||
} else {
|
|
||||||
iCurrentSpeed = iCurrentSpeed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
job = requestAnimationFrame(draw);
|
|
||||||
};
|
|
||||||
function createCanvas()
|
|
||||||
{
|
|
||||||
let container = document.createElement("div");
|
|
||||||
container.classList.add("boxmodel")
|
|
||||||
let canvas = document.createElement("canvas");
|
|
||||||
|
|
||||||
let text = document.createElement("div");
|
|
||||||
let text1 = document.createElement("div");
|
|
||||||
let text2 = document.createElement("div");
|
|
||||||
text.classList.add("text")
|
|
||||||
text1.classList.add("text1")
|
|
||||||
text2.classList.add("text2")
|
|
||||||
|
|
||||||
let mwse = document.createElement("div");
|
|
||||||
let text3 = document.createElement("span");
|
|
||||||
let text4 = document.createElement("span");
|
|
||||||
mwse.classList.add("mwse")
|
|
||||||
text3.classList.add("text3")
|
|
||||||
text4.classList.add("text4")
|
|
||||||
|
|
||||||
text.append(text1);
|
|
||||||
text.append(text2);
|
|
||||||
|
|
||||||
mwse.append(text3);
|
|
||||||
mwse.append(text4);
|
|
||||||
|
|
||||||
canvas.setAttribute("width", "440px");
|
|
||||||
canvas.setAttribute("height", "220px");
|
|
||||||
container.append(canvas);
|
|
||||||
container.append(text);
|
|
||||||
container.append(mwse);
|
|
||||||
createCanvas.value = canvas;
|
|
||||||
createCanvas.container = container;
|
|
||||||
createCanvas.ps = text1;
|
|
||||||
createCanvas.pw = text2;
|
|
||||||
createCanvas.rooms = text3;
|
|
||||||
createCanvas.clients = text4;
|
|
||||||
return container;
|
|
||||||
};
|
|
||||||
function update(value, pressure, rooms, clients)
|
|
||||||
{
|
|
||||||
iTargetSpeed = value;
|
|
||||||
createCanvas.ps.innerText = value;
|
|
||||||
createCanvas.pw.innerText = pressure + '%';
|
|
||||||
createCanvas.rooms.innerText = rooms;
|
|
||||||
createCanvas.clients.innerText = clients;
|
|
||||||
draw()
|
|
||||||
}
|
|
||||||
function convertSpeedToAngle(options) {
|
|
||||||
/* Helper function to convert a speed to the
|
|
||||||
* equivelant angle.
|
|
||||||
*/
|
|
||||||
let m = 150;
|
|
||||||
let value = (m/10) + Math.min(
|
|
||||||
Math.max(
|
|
||||||
options.speed,
|
|
||||||
0
|
|
||||||
),
|
|
||||||
240
|
|
||||||
);
|
|
||||||
iSpeedAsAngle = (
|
|
||||||
(100 / m) * value
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure the angle is within range
|
|
||||||
if (iSpeedAsAngle > 180) {
|
|
||||||
iSpeedAsAngle = iSpeedAsAngle - 180;
|
|
||||||
} else if (iSpeedAsAngle < 0) {
|
|
||||||
iSpeedAsAngle = iSpeedAsAngle + 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iSpeedAsAngle;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
createCanvas,
|
|
||||||
update
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
let elem = document.querySelector(".speed-container");
|
|
||||||
for(let e = 0; e < 8; e++)
|
|
||||||
{
|
|
||||||
let meter = CreateMeter();
|
|
||||||
let container = meter.createCanvas();
|
|
||||||
elem.appendChild(container);
|
|
||||||
meter.update(54,25)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
let elem = document.querySelector(".speed-container");
|
|
||||||
let meters = new Map();
|
|
||||||
async function reloadData()
|
|
||||||
{
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
await fetchData();
|
|
||||||
await new Promise(ok => {
|
|
||||||
setTimeout(() => ok(), 3000)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let isFirst = true;
|
|
||||||
async function fetchData()
|
|
||||||
{
|
|
||||||
let response = await fetch("/stats",{
|
|
||||||
method: "post",
|
|
||||||
credentials: "same-origin",
|
|
||||||
cache: "no-cache",
|
|
||||||
mode:"no-cors"
|
|
||||||
}).then(e => e.json());
|
|
||||||
for (const {
|
|
||||||
ws_total_packs,
|
|
||||||
core,
|
|
||||||
mwse_rooms,
|
|
||||||
mwse_clients
|
|
||||||
} of response) {
|
|
||||||
if(!meters.has(core))
|
|
||||||
{
|
|
||||||
let meter = CreateMeter();
|
|
||||||
let container = meter.createCanvas();
|
|
||||||
elem.appendChild(container);
|
|
||||||
meters.set(core, meter)
|
|
||||||
};
|
|
||||||
let _meter = meters.get(core);
|
|
||||||
_meter.update(
|
|
||||||
ws_total_packs,
|
|
||||||
ws_total_packs < 30 ? 0 : ws_total_packs < 60 ? 1 : ws_total_packs < 90 ? 2 : 3,
|
|
||||||
"RM: "+mwse_rooms,
|
|
||||||
"CL: "+mwse_clients
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reloadData();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
Reference in New Issue