WebDevice suite

This commit is contained in:
abdussamedulutas 2025-10-17 09:37:09 +03:00
parent 212e0151fa
commit 33b57d7e67
14 changed files with 1254 additions and 14 deletions

View File

@ -5,7 +5,7 @@ process.on('message',data => {
switch(data.type)
{
case "CLIENT_CREATED":{
slog("CLIENT_CREATED");
//slog("CLIENT_CREATED");
let client = new Client();
client.isProxy = true;
client.proxyProcess = data.pid;
@ -22,7 +22,7 @@ process.on('message',data => {
data.value = transformDeserialization(data.value, data.typing);
slog("CLIENT_UPDATE_PROP");
//slog("CLIENT_UPDATE_PROP");
let client = Client.clients.get(data.uuid);
client[data.name] = data.value;
break;
@ -44,7 +44,7 @@ process.on('message',data => {
break;
}
case "CLIENT_DESTROY":{
slog("CLIENT_DESTROY");
//slog("CLIENT_DESTROY");
if(Client.clients.has(data.uuid))
{
Client.clients.delete(data.uuid);
@ -54,7 +54,7 @@ process.on('message',data => {
break;
}
case "ROOM_CREATED":{
slog("ROOM_CREATED");
//slog("ROOM_CREATED");
let room = Room.fromJSON(data.value);
Room.rooms.set(room.id, room);
break;
@ -64,13 +64,13 @@ process.on('message',data => {
data.value = transformDeserialization(data.value, data.typing);
slog("ROOM_UPDATE_PROP");
//slog("ROOM_UPDATE_PROP");
let room = Room.rooms.get(data.uuid);
room[data.name] = data.value;
break;
}
case "ROOM_JOIN_CLIENT":{
slog("ROOM_JOIN_CLIENT");
//slog("ROOM_JOIN_CLIENT");
let room = Room.rooms.get(data.uuid);
let client = Client.clients.get(data.client);
if(room && client)
@ -81,7 +81,7 @@ process.on('message',data => {
break;
}
case "ROOM_EJECT_CLIENT":{
slog("ROOM_EJECT_CLIENT");
//slog("ROOM_EJECT_CLIENT");
let room = Room.rooms.get(data.uuid);
let client = Client.clients.get(data.client);
if(room && client)
@ -92,7 +92,7 @@ process.on('message',data => {
break;
}
case "ROOM_DESTROY":{
slog("ROOM_DESTROY");
//slog("ROOM_DESTROY");
Room.rooms.delete(data.value);
break;
}

View File

@ -1,8 +1,9 @@
import WebRTC from "./WebRTC";
import Peer from "./Peer";
/**
* Deneyseldir kullanılması önerilmez
*/
export default class P2PFileSender
{
public rtc : RTCPeerConnection;

View File

@ -31,7 +31,7 @@ export default class WebRTC
rtcpMuxPolicy:"require",
};
public isPolite() : boolean
private isPolite() : boolean
{
let myId = this.peer?.mwse.peer('me').socketId as string;
let peerId = this.peer?.socketId as string;

25
public/core.js Normal file
View File

@ -0,0 +1,25 @@
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();
};

86
public/studio.html Normal file
View File

@ -0,0 +1,86 @@
<!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>

View File

@ -0,0 +1,398 @@
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);
}
}

65
public/studio/Rooms.js Normal file
View File

@ -0,0 +1,65 @@
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">
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.

After

Width:  |  Height:  |  Size: 660 KiB

162
public/studio/cssom.js Normal file
View File

@ -0,0 +1,162 @@
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;
/*
*/

44
public/studio/sdb.js Normal file
View File

@ -0,0 +1,44 @@
/**
* @ 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;

293
public/studio/window.js Normal file
View File

@ -0,0 +1,293 @@
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");
}
}

View File

@ -12,9 +12,9 @@ async function beginEngine()
accessType: "private",
description: "Private free joined room",
ifexistsJoin: false,
notifyActionEjected: true,
notifyActionEjected: false,
notifyActionInvite: false,
notifyActionJoined: true
notifyActionJoined: false
});
try{
await room.createRoom();

166
public/trial.html Normal file
View File

@ -0,0 +1,166 @@
<!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>

File diff suppressed because one or more lines are too long