Sindbad~EG File Manager
/* code to handle two-finger dragging on a trackpad using the 'wheel' event.
The only way to know when the user is done scrolling is to use a timer
to detect when 200 ms has elapsed without a wheel event.
In mousemove, e.deltaX is the change since mousedown; but with
wheel, e.deltaX is the change since the last wheel event. Therefore
we have to keep track of the cumulative change ourselves. */
let scrollState = new Map(); // Map to track cumulative scroll offsets
let scrollTimer; // Timer to detect scroll completion
let currentTransform; // transform to measure total scrolling
function scrollGraphs(e){
// scroll all the graphs that contain (e.clientX, e.clientY)
x = e.clientX;
y = e.clientY;
let k = 0;
firstGraphIndex = -1;
let graphs = document.querySelectorAll('.graph');
for (let graph of graphs) {
// Use the manually defined bounds instead of getBoundingClientRect()
let graphX = parseInt(graph.getAttribute("left"), 10);
let graphY = parseInt(graph.getAttribute("top"), 10);
let graphWidth = parseInt(graph.getAttribute("mywidth"), 10);
let graphHeight = parseInt(graph.getAttribute("myheight"), 10);
if (x >= graphX && x <= graphX + graphWidth && y >= graphY && y <= graphY + graphHeight)
{ if(firstGraphIndex == -1)
firstGraphIndex = k;
var dx = e.deltaX;
var dy = e.deltaY;
if(firstGraphIndex === -1)
firstGraphIndex = k; // this will be used on mouseup to know which graph to scroll
const elmnt = graph.querySelector('g.scrollable'); // the scrollable <g> of the graph
// Retrieve the current transform state or initialize it
currentTransform = scrollState.get(elmnt) || { x: 0, y: 0 };
// Update transform values
currentTransform.x += dx;
currentTransform.y += dy;
// Apply the updated transform
const transform = `translate(${currentTransform.x}, ${currentTransform.y})`;
elmnt.setAttribute('transform', transform);
// Update the scroll state
scrollState.set(elmnt, currentTransform);
}
++k;
}
}
// Add wheel event listener for scrolling with two fingers
svgContainer.addEventListener("wheel", function (e) {
if (e.ctrlKey || e.deltaY % 1 !== 0)
return; // this is a pinch-to-zoom event, ignore it here.
// Prevent default scrolling behavior
e.preventDefault();
e.stopPropagation();
// Handle movement of relevant graphs
scrollGraphs(e);
// Reset the scroll completion timer
if (scrollTimer) {
clearTimeout(scrollTimer);
}
scrollTimer = setTimeout(() => {
console.log("Scrolling completed, synchronizing with the engine");
// get the element to scroll
elmnt = document.getElementById("scrollable"+firstGraphIndex);
let deltax = currentTransform.x;
let deltay = currentTransform.y;
if(deltax == 0 && deltay == 0)
return; // don't bother telling the Engine nothing happened
if (isNaN(deltax) || isNaN(deltay))
return;
// Next we will submit a form that, when processed, will
// send a graphMoved message to the Engine
// Form the parameter in the required format
let graphwidth = <?php echo($_SESSION['graphWidth']);?>;
let height = <?php echo($_SESSION['height']);?>;
const param = `[${firstGraphIndex},${deltax},${deltay},${graphwidth},${height}]`;
// Prepare the data string with the required parameter name
// submit a hidden form so param will become $_POST['graphMovedParam']
let form = document.createElement('form');
form.method = 'POST';
form.action = "<?php echo ($_SERVER['PHP_SELF']); ?>";
// Create a hidden input to hold the param value
let input = document.createElement('input');
input.type = 'hidden';
input.name = 'graphMovedParam';
input.value = param;
// Append the input to the form
form.appendChild(input);
// Append the form to the body
document.body.appendChild(form);
// Submit the form
form.submit();
}, 200); // 200 ms delay for detecting scroll completion
},
{ passive: false } // an option, third argument of setEventListener. Set passive to false so preventDefault works
);
/*____________________________________________________________________________________*/
let scale = 1.0;
let zoomTimer; // Timer to detect zoom completion
function syncZoom(s,x,y){
// s is the zoom scale factor, x and y the pixel viewport coordinates of the zoom center.
// the viewport is the rectangle containing all the graphs but not the Toolbar.
// Send a zoomAtPoint message to the Engine. The parameter must be [s,x,y,graphwidth,height]
// Form the parameter in the required format
let graphwidth = <?php echo($_SESSION['graphWidth']);?>;
let height = <?php echo($_SESSION['height']);?>;
const param = `[${s},${x},${y},${graphwidth},${height}]`;
// Prepare the data string with the required parameter name
// submit a hidden form so param will become $_POST['zoomAtPointParam']
let form = document.createElement('form');
form.method = 'POST';
form.action = "<?php echo ($_SERVER['PHP_SELF']); ?>";
// Create a hidden input to hold the param value
let input = document.createElement('input');
input.type = 'hidden';
input.name = 'zoomAtPointParam';
input.value = param;
// Append the input to the form
form.appendChild(input);
// Append the form to the body
document.body.appendChild(form);
// console.log("ready to submit form with parameter ", param); // Ok to here
// Submit the form
form.submit();
}
/*____________________________________________________________________________________*/
function zoomGraphs(e) {
let zoomFactor = e.deltaY > 0 ? 0.05 : -0.05 // Zoom in or out
e.preventDefault();
e.stopPropagation();
scale += zoomFactor;
toolbarwidth = <?php echo($toolbarwidth); ?>;
//console.log("toolbarwidth=", toolbarwidth);
let x = e.clientX - toolbarwidth; // coordinate of mouse relative to edge of browser window
let y0 = e.clientY;
if (scale > 2)
{ scale = 2;
return;
}
if( scale < 0.5)
{ scale = 0.5;
return; // syncZoom(scale,x,y0);
}
for (let graph of graphs) {
// Check if the point is within the bounds of the graph
const graphWidth = parseFloat(graph.getAttribute("mywidth"), 10); // width of (invisible and visible) graph
const graphY = parseFloat(graph.getAttribute("top"),10);
let graphX = parseFloat(graph.getAttribute("left"),10); // for now, left of visible part of graph
graphX += toolbarwidth; // without this it is measured in svgContainer, but we want browser-window coordinates
const graphHeight = parseFloat(graph.getAttribute("myheight"), 10);
let y = e.clientY;
console.log("x=",x);
console.log("y=",y);
console.log("graphWidth=",graphWidth);
console.log("graphY=", graphY);
console.log("graphHeight=",graphHeight);
console.log("scale =", scale);
if (graphX <= x && x <= graphX + graphWidth && graphY <= y && y <= graphY + graphHeight) {
const elmnt = graph.querySelector('g.scrollable'); // the scrollable <g> of the graph
// Compute the translation to keep the zoom centered at the cursor
// scaling applies to elmnt, which is the same size and shape as the graph
console.log("got here");
const translateX = -x;
const translateY = -y;
const lambda = 1/scale;
const transformString = `translate(${x},${y}) scale(${lambda}) translate(${translateX},${translateY})`;
console.log("transform = ", transformString);
elmnt.setAttribute("transform", transformString);
}
}
}
/*_____________________________________________________________________________________*/
/* following code detects pinch-for-zoom events and responds by zooming the graph(s) that
contains the mouse point. */
svgContainer.addEventListener(
"wheel",
(e) => {
if (!e.ctrlKey && e.deltaY % 1 == 0)
return; /// not a pinch-to-zoom event
if( isNaN(e.deltaX) || isNaN(e.deltaY))
return;
e.preventDefault(); // Prevent default browser zoom behavior
zoomGraphs(e);
// Reset the zoom completion timer
if (zoomTimer) {
return; //clearTimeout(zoomTimer);
}
const mouseX = e.clientX;
const mouseY = e.clientY;
zoomTimer = setTimeout(() => {
console.log("zooming completed, synchronizing with the engine");
toolbarwidth = <?php echo($toolbarwidth); ?>;
//console.log("toolbarwidth=", toolbarwidth);
let x = mouseX - toolbarwidth; // coordinate of mouse relative to edge of browser window
let y0 = mouseY;
syncZoom(scale,x,y0);
zoomTimer = null;
}, 300
);
},
{ passive: false } // Ensure preventDefault works
);
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists