WebDevice suite
This commit is contained in:
parent
212e0151fa
commit
33b57d7e67
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import WebRTC from "./WebRTC";
|
||||
import Peer from "./Peer";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deneyseldir kullanılması önerilmez
|
||||
*/
|
||||
export default class P2PFileSender
|
||||
{
|
||||
public rtc : RTCPeerConnection;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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">
|
||||
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.
|
After Width: | Height: | Size: 660 KiB |
|
|
@ -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;
|
||||
/*
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue