Sindbad~EG File Manager
<?php
// Set HTTP header to prevent caching
header("Cache-Control: no-cache, must-revalidate");
// Start or resume the session
session_start();
$sessionId = session_id(); // guaranteed not to contain a pipe character
ini_set('display_errors', 1);
error_reporting(E_ALL);
$serverAddress = 'localhost'; // Adjust the server address
$serverPort = 12349; // Adjust the server port. Ending in 9 for the Engine; in 7 for Polygon
$timeout = 3600; // Connection timeout in seconds. If the server does not respond by then, close the socket.
$startupDelay = 5; // Delay for server startup in seconds if the server is not already running
$imageBaseDir = "/Users/beeson/Dropbox/MathXpert/images/";
if (!(isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) {
$redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $redirect);
exit();
}
require("SendMessage.php");
$clientSocket = createClientSocket($serverAddress, $serverPort, $timeout);
// Handle Ajax requests before emitting HTML
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{ $response = "nothing";
if ( isset($_POST['graphTitleMovedParam']))
{
$param = $_POST['graphTitleMovedParam'];
// Process the AJAX request
$response = sendMessage($clientSocket,"graphTitleMoved",$param);
// We do not care what the response is as long as it is not false
}
else if ( isset($_POST['askPointSlopeParam']))
{
$param = $_POST['askPointSlopeParam'];
// Process the AJAX request
$pointSlopeResponse = sendMessage($clientSocket,"askPointSlope",$param);
echo($pointSlopeResponse);
exit; // without this, the entire document GetProb.php produces with this $_POST
// would be sent to the listener waiting for the Ajax result.
}
else if ($response != "nothing")
{ // $response is the response to the Ajax request
// echo($response); // this will display an error message for the developer if an incorrect parameter was sent.
exit; // Stop further processing, so the rest of the HTML isn't sent
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=0">
<title>MathXpert Grapher</title>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
border: none;
}
/* Set a global font-family rule for all text elements. Only Times New Roman actually works well. */
text {
font-family: 'Times New Roman', 'STIX Two Math', 'Cambria Math', 'Latin Modern Math', 'TeX Gyre Termes Math';
}
svg text {
font-family: 'Times New Roman';
}
/* the following container with horizontal flow will hold the Toolbar and the svgContainer,
so the svgContainer will come to the right of the Toolbar */
.container {
display: flex;
align-items: flex-start; /* begin each column at the top */
/* the first column contains the Toolbar, so this gets the Toolbar at the top.
position:relative; */
top:0;
left:0;
}
/* Define svgContainer div so it comes to the right of the Toolbar.
We have to have either absolute or relative positioning here so that the SVG output of the Engine will be
interpreted relative to svgContainer. If we use position:absolute, then we must also specify left and top.
Using postion:relative allows flex layout in .container to place svgContainer to the
right of the Toolbar without hardcoding the width of the Toolbar.
*/
#svgContainer {
position:relative;
z-index: 1;
text-shadow: none;
}
.popup svg {
width: 100%;
height: auto;
word-wrap: break-word;
white-space: pre-wrap;
}
#titleRect {
position: absolute;
cursor: move;
border: none;
background-color: transparent;
text-shadow: none;
}
[id^="draggableTitle"] { /* applies to draggableTitle0, draggableTitle1, etc. */
position: absolute;
cursor: move;
border: none;
background-color: transparent;
text-shadow: none;
padding: 5px;
color: black;
top: 10px;
left: 10px; /* Initial position */
z-index: 2; /* Ensure it's above other elements */
}
#PointSlopeDiv {
position: absolute;
cursor: move;
border: 5px;
background-color: transparent;
border-color: transparent;
text-shadow: none;
padding: 5px;
color: black;
top: 50px;
left: 50px; /* Initial position */
width:auto;
height:auto;
z-index: 2; /* Ensure it's above other elements */
}
#crosshairs-container {
position: absolute;
top: 0;
left: 0;
display:none;
background-color:transparent;
pointer-events: none; /* Allow click events to pass through */
}
#selectionRectangle {
position: absolute;
border: 1px solid red;
pointer-events: none; /* Ignore this element during mouse events */
}
body {
background-color:none;
}
/* Style for the modal */
#hintModal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0);
justify-content: center;
align-items: center;
}
/* Style for the modal content */
#modalContent {
background: rgb(200,200,255);
padding: 20px;
border-radius: 5px;
width: 50%;
height: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
</style>
</head>
<?php
if (!(isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' ||
$_SERVER['HTTPS'] == 1) ||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'))
{
$redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $redirect);
exit();
}
?>
<body>
<?php
function decode($response,$fillcolor){
// To save bandwidth we've compressed some SVG commands, now expand them
// This only saves bandwidth between the Engine and the PHP code, not between the web server and browser,
// but otherwise the 1 megabyte limit on messages between the Engine and PHP is broken, i.e., the length of
// messages needed to draw these graphs is more than 6 digits. It's easier
// just to do this decoding than alter the basic Engine message mechanisms.
$pattern = '/<q (-?\d+) (-?\d+)>/';
return preg_replace_callback(
$pattern,
function ($matches) use ($fillcolor) { // Use "use" to inherit $fillcolor
$x = $matches[1] - 0.5;
$y = $matches[2] - 0.5;
return "<circle cx=\"{$x}\" cy=\"{$y}\" r=\"1\" style=\"fill:{$fillcolor};stroke:none;\" />\n";
},
$response
);
}
?>
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST")
{ if(isset($_SESSION['width']))
// in case width and height don't get set below, let's make sure they are set.
$width = $windowWidth = $_SESSION['width'] ;
if(isset($_SESSION['height']))
$height = $_SESSION['height'];
$fillcolor = "rgb(128,128,255)";
if(isset($_SESSION['toolbarwidth']))
{ $toolbarwidth = $_SESSION['toolbarwidth'];
$graphwidth = $width - $toolbarwidth;
}
if (isset($_POST['problemnumberField']))
// this means the problemsource is SOURCE_MATHXPERT, as typed-in problems don't have numbers
{
$problemNumber = intval($_POST["problemnumberField"]);
$topicNumber = intval($_POST["topicField"]);
$sender = "GetProblem";
$language = $_POST["language"];
$windowWidth = $_SESSION['width'] = intval($_POST["widthField"]); // width of the browser window
$toolbarwidth = $_SESSION['toolbarwidth'] = intval($_POST["toolbarwidthField"]);
$width = $windowWidth;
// echo("width = $width and toolbarwith = $toolbarwidth");
$height = $_SESSION['height'] = intval($_POST["heightField"]); // height of the browser window
$_SESSION['languagenumber'] = $language;
$_SESSION['width'] = $width;
$_SESSION['height'] = $height;
$_SESSION['topicNumber'] = $topicNumber;
$_SESSION['problemNumber'] = $problemNumber;
}
if (isset($_POST["widthField2"])) // 'graphButton_x' doesn't work!
{
$sender = "graphButton";
$graphwidth = $_SESSION['graphwidth'] = intval($_POST["widthField2"]);
$toolbarwidth = $_SESSION['toolbarwidth'] = intval($_POST["toolbarwidthField"]);
$height = $_SESSION['height'] = intval($_POST["heightField2"]);
$windowWidth = $graphwidth + $toolbarwidth;
$width = $windowWidth;
// echo("width = $width and toolbarwith = $toolbarwidth and graphwidth = $graphwidth");
}
if (isset($_POST['language2']))
{
$sender = "setLanguage";
$language = $_POST['language2'];
}
if (isset($_POST['action'])) {
$sender = $_POST['action'];
// Now, $sender should contain something like "parameterplus0"
}
if (isset($_POST['horizontalzoomout']))
{
$sender = "horizontalzoomout";
}
if (isset($_POST['horizontalzoomin']))
{
$sender = "horizontalzoomin";
}
if (isset($_POST['verticalzoomout']))
{
$sender = "verticalzoomout";
}
if (isset($_POST['verticalzoomin']))
{
$sender = "verticalzoomin";
}
if (isset($_POST['doublezoomout']))
{
$sender = "doublezoomout";
}
if (isset($_POST['doublezoomin']))
{
$sender = "doublezoomin";
}
if ( isset($_POST['graphMovedParam']))
{
$param = $_POST['graphMovedParam'];
$sender = "graphMoved";
$_SESSION['mouseFunction'] = 1; // 1 is mouseScrollGraph
}
if (isset($_POST['selectedRectangleField']) && !empty($_POST['selectedRectangleField']))
{
$selectedRectangleParameter = $_POST['selectedRectangleField'];
$_SESSION['mouseFunction'] = $_POST['mouseFunctionField'];
// so it can be restored after Draw causes sendGraphDocumnt
$sender = "draw";
}
if (isset($_POST['graphWindowResizedField']))
{ $param = $_POST['graphWindowResizedField'];
$sender = "graphWindowResized";
$newgraphwidth = $_POST['graphwidth'];
$_SESSION['graphwidth'] = $newgraphwidth;
$_SESSION['height'] = $_POST['newHeight'];
}
if(isset($_POST['directionField']))
{
$sender = "toggleDirectionField";
}
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false)
{
echo "Socket creation failed: " . socket_strerror(socket_last_error()) . "<br>";
}
else
{
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => $timeout, "usec" => 0));
$result = socket_connect($socket, $serverAddress, $serverPort);
if(isset($_SESSION['graphwidth']))
{
$graphwidth = $_SESSION['graphwidth'];
$width = $graphwidth + $toolbarwidth;
}
else if(isset($_SESSION['width']))
{
$width = $_SESSION['width'] ;
$graphwidth = $width-$toolbarwidth;
}
if(isset($_SESSION['height']))
$height = $_SESSION['height'];
// echo("$width $toolbarwidth $graphwidth");
$fillcolor = "rgb(128,128,255)";
if ($result)
{ if($sender=="GetProblem")
{ $param = $topicNumber . "+" . $problemNumber . "+" . $language . "+" . $graphwidth . "+" . $height;
unset($_SESSION['mouseFunction']);
$response = sendMessage($clientSocket, "initGraphDocFromLibrary", $param);
// echo("topic number is $topicNumber");
// if ($topicNumber == 21 || $topicNumber == 26) { // MC_SET or graph_relation
if(strstr("<q",$response))
{
$response = decode($response,$fillcolor);
}
}
else if ($sender == "graphButton")
{
$param = $graphwidth . "+" . $height;
$response = sendMessage($clientSocket,"graphButton", $param);
}
else if($sender == "setLanguage")
{
$param = $language;
$_SESSION['languagenumber'] = $language;
}
else if( str_starts_with($sender, "parameterplus"))
{
$index = $sender[13]; // the character after parameterplus
$param = $graphwidth . "+" . $height. "+" . $index;
$response = sendMessage($clientSocket,"incrementParameter", $param);
}
else if( str_starts_with($sender, "parameterminus"))
{
$index = $sender[14]; // the character after parameterminus
$param = $graphwidth . "+" . $height. "+" . $index;
$response = sendMessage($clientSocket,"decrementParameter", $param);
}
else if ($sender == "draw")
{
$param = $selectedRectangleParameter;
$response = sendMessage($clientSocket,"selectedRectangleGraph", $param);
}
else if ($sender == "graphMoved")
{
$response = sendMessage($clientSocket,"graphMoved", $param);
}
else if ($sender == "graphWindowResized")
{
$response = sendMessage($clientSocket,"graphWindowResized", $param);
}
else
{
if(!is_string($sender) || strlen($sender) == 0)
$response = false;
else
{
$param = $graphwidth . "+" . $height;
$response = sendMessage($clientSocket,$sender,$param);
}
}
if ($response === false || $response == null)
{
$errcode = socket_last_error($socket);
$message = socket_strerror($errcode);
echo "Socket_read error: $message<br>";
}
else if (strpos($response, 'ERROR:') === 0)
{ // It's an error message
$errorMessage = substr($response, strlen('ERROR:'));
echo "<div class='error'>Error: {$errorMessage}</div>";
}
else
{
// The response consists of SVG, to redraw the whole window. It has to go into svgContainer.
if(strstr($response,"<q "))
$response = decode($response,$fillcolor);
?>
<div class="container" id="wholeWindow">
<?php include 'toolbar.php'; ?>
<script>
// Embed PHP variables into JavaScript
// After the DOM is loaded, AdjustToolbar will remove unwanted buttons
var problemNumber = "<?php echo intval($_SESSION['problemNumber']); ?>";
var topicNumber = "<?php echo intval($_SESSION['topicNumber']); ?>";
</script>
<script src="AdjustToolbar.js"></script>
<div id="svgContainer">
<div id="PointSlopeDiv" style="position: absolute; cursor: move; border: 5px; background-color: transparent; padding: 5px; left:50px; top:50px"></div>
<div id="crosshairs-container">
<svg width="38" height="38">
<!-- Vertical lines -->
<line x1="19" y1="0" x2="19" y2="18" stroke="black" stroke-width="1"/>
<line x1="19" y1="20" x2="19" y2="38" stroke="black" stroke-width="1"/>
<!-- Horizontal lines -->
<line x1="0" y1="19" x2="18" y2="19" stroke="black" stroke-width="1"/>
<line x1="20" y1="19" x2="38" y2="19" stroke="black" stroke-width="1"/>
</svg>
</div>
<div id="draggableTitle0" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px"></div>
<div id="draggableTitle1" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px""> </div>
<div id="draggableTitle2" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px"></div>
<div id="draggableTitle3" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px""> </div>
<div id="draggableTitle4" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px"></div>
<div id="draggableTitle5" style="position: absolute; cursor: move; border: none; background-color: transparent; padding: 5px; left:10px; top:10px""> </div>
<svg id="graphSVG" width="<?php echo $graphwidth; ?>" height="<?php echo $height; ?>" top = "0px ?>"; xmlns="http://www.w3.org/2000/svg">
<?php echo $response; ?>
</svg>
</div> <!-- This closes the svgContainer div -->
</div> <!-- This closes the container div -->
<?php
}
socket_close($socket); // because server has already closed it
} // close if($result)
else
{
echo "Failed to connect to the C program: " . socket_strerror(socket_last_error()) . "<br>";
}
}
}
?>
<script src="wrapSVGtext.js"></script>
<script src="commentaryModal.js"></script>
<script src="crashPopup.js"></script>
<script>
// The mouse can perform different functions. Which one it is
// used for is controlled by these variables, which are changed
// by buttons on the Graph Toolbar
const mouseScrollGraph = 1;
const mouseSelectRectangle = 2;
const mouseSelectCenteredRectangle = 3;
const mousePointSlope = 4;
let mouseFunction = <?php if(isset($_SESSION['mouseFunction'])) echo($_SESSION['mouseFunction']); else echo("1"); ?>;
// 1 is mouseScrollGraph, the default. Have to use the number in PHP as mouseScrollGraph is a Javascript variabe
let pointSlopeOn = false;
// Code to drag the titles
// Make the draggableTitle elements draggable (always, this can't be turned on or off)
dragElement(document.getElementById("draggableTitle0"));
dragElement(document.getElementById("draggableTitle1"));
// also the PointSlope window
dragElement(document.getElementById("PointSlopeDiv"));
function dragElement(elmnt) { // make elmnt draggable
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
var oldmouseFunction = mouseFunction;
if (document.getElementById(elmnt.id))
{
// If present, the header is where you move the element from:
document.getElementById(elmnt.id).onmousedown = dragMouseDown;
}
else
{
// Otherwise, move the DIV from anywhere inside the DIV:
elmnt.onpointerdown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault(); // Prevents default action (e.g., text selection)
e.stopPropagation(); // Prevents event from bubbling up to parent elements
// Get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// Call a function whenever the cursor moves:
document.onpointermove = elementDrag;
isSelecting = false; // prevent selection rectangle
oldmouseFunction = mouseFunction; // to be restored on mouseup
// apparently e.stopPropagation() on mouseup does not prevent mouseup from reaching the svgContainer.
mouseFunction = -1; // not a valid value!
console.log("Setting isSelecting to false");
console.log(elmnt);
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault(); // Prevents default action (e.g., text selection)
e.stopPropagation(); // Prevents event from bubbling up to parent elements
// Calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// Set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
console.log(elmnt.style.left);
}
function closeDragElement(e) {
e = e || window.event;
e.preventDefault(); // Prevents default action (e.g., text selection)
e.stopPropagation(); // Prevents event from bubbling up to parent elements
// Stop moving when mouse button is released:
document.onpointerup = null;
document.onpointermove = null;
console.log("In closeDragElement");
// Get the element's ID and extract the index
var id = elmnt.id;
var index = id.match(/\d+$/)[0]; // Extract the number at the end of the ID
// Get the element's current position and size
var rect = elmnt.getBoundingClientRect();
var x1 = rect.x-60;
var y1 = rect.y;
var x2 = rect.right-60;
var y2 = rect.bottom;
console.log(rect,x1,y1,x2,y2);
// Form the string in the required format
var param = `[${index},${x1},${y1},${x2},${y2}]`;
// Prepare the data string with the required parameter name
var data = `graphTitleMovedParam=${encodeURIComponent(param)}`;
// Send AJAX message notifying the Engine
var xhr = new XMLHttpRequest();
xhr.open("POST", "<?php echo ($_SERVER['PHP_SELF']); ?>", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
console.log('Data being sent:', data);
xhr.send(data);
mouseFunction = oldmouseFunction; // restore the value it had on mousedown
}
}
// End of title-dragging code.
// The following function is used to put SVG output into whatever container
// the interface designers choose.
function moveSvgElementToContainer(svgElementId, destinationContainerId) {
var svgElement = document.getElementById(svgElementId);
var destinationContainer = document.getElementById(destinationContainerId);
if (svgElement && destinationContainer) {
destinationContainer.style.position = 'absolute';
// destinationContainer.style.left = svgElement.style.left;
// This should be done by the calling function
// destinationContainer.style.top = svgElement.style.top;
var svgRect = svgElement.getBoundingClientRect();
destinationContainer.style.width = svgRect.width + 'px';
destinationContainer.style.height = svgRect.height + 'px';
// Now move the SVG element to the destination container
destinationContainer.appendChild(svgElement);
// Since the SVG is now positioned relative to its new container, reset its position
svgElement.style.left = '0px';
svgElement.style.top = '0px';
}
else
{
if(svgElement)
{
console.error('destination container not found');
}
else
{
console.error('SVG element not found');
console.error(svgElementId);
}
}
}
// implement the Toolbar buttons that don't require that the Engine resend the document:
function turnOnMouseScroll(){
mouseFunction = mouseScrollGraph;
console.log("mouseFunction is", mouseFunction);
if (svgContainer.contains(selectionRectangle))
svgContainer.removeChild(selectionRectangle);
}
function turnOnSelectRectangle(){
mouseFunction = mouseSelectRectangle;
console.log("mouseFunction is", mouseFunction);
}
function turnOnSelectCenteredRectangle(){
mouseFunction = mouseSelectCenteredRectangle;
console.log("mouseFunction is", mouseFunction);
}
function turnOnPointSlope(){
mouseFunction = mousePointSlope;
// pointSlopeOn = true; Not yet, only on mousedown
console.log("mouseFunction is", mouseFunction);
}
function showAssumptions() {
// Create a popup window
var popupWindow = window.open('', 'Assumptions', 'width=600, height=400');
// Create a div with relative positioning
var div = popupWindow.document.createElement('div');
div.style.position = 'relative';
// Append the div to the popup window
popupWindow.document.body.appendChild(div);
// Display all SVG elements with class "assumption"
var assumptions = document.querySelectorAll('.assumption');
assumptions.forEach(function (assumption) {
// Clone the assumption
var clonedAssumption = assumption.cloneNode(true);
// Set the display property to block (or any other appropriate value)
clonedAssumption.style.display = 'block';
// Append the cloned assumption to the div
div.appendChild(clonedAssumption);
});
}
function showSingularities() {
// Create a popup window
var popupWindow = window.open('', 'Singularities', 'width=600, height=400');
// Create a div with relative positioning
var div = popupWindow.document.createElement('div');
div.style.position = 'relative';
// Append the div to the popup window
popupWindow.document.body.appendChild(div);
// Display all SVG elements with class "singularity"
var singularities = document.querySelectorAll('.singularity');
singularities.forEach(function (t) {
var clonedSingularity = t.cloneNode(true);
// Set the display property to block (or any other appropriate value)
clonedSingularity.style.display = 'block';
// Append the cloned assumption to the div
div.appendChild(clonedSingularity);
});
}
function showJumps() {
// Create a popup window
var popupWindow = window.open('', 'Singularities', 'width=600, height=400');
// Create a div with relative positioning
var div = popupWindow.document.createElement('div');
div.style.position = 'relative';
// Append the div to the popup window
popupWindow.document.body.appendChild(div);
// Display all SVG elements with class "jump"
var jumps = document.querySelectorAll('.jump');
jumps.forEach(function (t) {
var clonedJump = t.cloneNode(true);
// Set the display property to block (or any other appropriate value)
clonedJump.style.display = 'block';
// Append the cloned assumption to the div
div.appendChild(clonedJump);
});
}
function svgToImageInput(svgElementId, inputElementId) {
var svgElement = document.getElementById(svgElementId);
var inputElement = document.getElementById(inputElementId);
// Serialize SVG to string
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgElement);
// Encode SVG string in Base64
var svgBase64 = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgString)));
// Set the encoded SVG as the source for the input image
inputElement.setAttribute('src', svgBase64);
}
// Ensure this script runs after the DOM is fully loaded
document.addEventListener('DOMContentLoaded', function() {
let title0 = document.getElementById('title0');
if (title0) {
let left = title0.style.left;
let top = title0.style.top;
// now (left,top) is where we want draggableTitle0 to be
let draggableTitle0 = document.getElementById("draggableTitle0");
if (draggableTitle0) {
draggableTitle0.style.left = left;
draggableTitle0.style.top = top;
moveSvgElementToContainer('title0', 'draggableTitle0');
moveSvgElementToContainer('titleRect0', 'draggableTitle0');
// that changes title0.left and title0.top to 0
}
}
// Now do the same for title1 through title5 if they exist
for (let k = 1; k < 6; k++) {
// 6 is MAXGRAPHS in the Engine
let id = "title" + k;
let title = document.getElementById(id);
if (title === null)
{ // console.log("breaking because title doesn't exist");
break;
// This works with === but not with ==, which I think is a Javascript bug
}
let titleRectId = "titleRect" + k;
let titleRect = document.getElementById(titleRectId);
let draggableTitleId = "draggableTitle" + k;
let draggableTitle = document.getElementById(draggableTitleId);
let left = title.style.left;
let top = title.style.top;
draggableTitle.style.left = left;
draggableTitle.style.top = top;
if (draggableTitle) {
moveSvgElementToContainer(id, draggableTitleId);
moveSvgElementToContainer(titleRectId, draggableTitleId);
}
}
});
let selectionMade = false; // set it to true on mouse_up when a rectangle is selected
document.addEventListener('DOMContentLoaded', function() {
showCrashPopup(); // if a crash has occurred, notify the user.
// Give the Draw button something to do when it's clicked:
document.getElementById('draw').addEventListener('click', function(event) {
// Construct the selected rectangle string
// The form will submit automatically as this button is of type image/input inside the form. Therefore:
event.preventDefault(); // Prevent the default form submission
console.log("Draw button was clicked");
console.log("Value of selectionMade = ");
console.log(selectionMade);
if (selectionMade) { // Check if a selection has been made
let windowwidth = <?php echo($_SESSION['width']);?>;
let windowheight = <?php echo($_SESSION['height']);?>;
let toolbarwidth = <?php echo($toolbarwidth);?>;
// console.log(toolbarwidthpx);
let selectedRectangleValue = "[" + [whichgraph, parseInt(selectedX), parseInt(selectedY), parseInt(selectedWidth), parseInt(selectedHeight),parseInt(toolbarwidth), windowwidth, windowheight].join(',') + "]";
// coordinate parameters are expected by the Engine to be integers either all in pixels or all without units. Here, all are withouit units, as shown by console.log.
// console.log(selectedRectangleValue)
// return; // use this to see the console.log output
document.getElementById('selectedRectangleField').value = selectedRectangleValue;
document.getElementById('mouseFunctionField').value = mouseFunction;
// Now submit it
selectionMade = false; // we're done with it
document.getElementById('myForm').submit();
} // end if
} // end of the function
); // end of addEventListener
// Create and label buttons to increment and decrement parameters
// Step 1: Identify all SVG elements of class "param"
const svgs = document.querySelectorAll('svg.param');
// Step 2 & 3: Process each SVG and create corresponding button groups
svgs.forEach((svg, index) => {
/* // this code saved in case it's someday useful; ...
// but not used here because can't color an image in CSS
// Convert SVG to Data URL
const xml = new XMLSerializer().serializeToString(svg);
const svg64 = btoa(unescape(encodeURIComponent(xml)));
const svgDataUrl = 'data:image/svg+xml;base64,' + svg64;
svg.style.display = "none"; // Hide the original SVG to prevent clutter.
// svg.parentNode.removeChild(svg);
const inputImage = document.createElement('input');
inputImage.type = 'image';
inputImage.src = svgDataUrl;
inputImage.alt = `Parameter ${index}`;
inputImage.style.width = '24px';
inputImage.style.height = '24px';
*/
// Create button group container with specified styles
const buttonGroup = document.createElement('div');
buttonGroup.className = 'button-group';
buttonGroup.style.display = 'flex';
buttonGroup.style.flexDirection = 'row';
buttonGroup.style.alignItems = 'center';
// Create the main button with SVG in it and specified styles
const button = document.createElement('button');
button.type = 'button'; // Use 'button' type for generic buttons
button.alt = `Parameter ${index}`;
button.id = `parameterButton${index}`;
button.style.width = '24px';
button.style.height = '24px';
button.style.backgroundColor = 'rgb(0,0,128)';
button.style.color = 'white';
button.classList.add('svg-button'); // Add a class for consistent styling
// change the color of the svg
svg.querySelectorAll('text').forEach(textElement => {
textElement.style.fill = 'white'; // Set the text color to white
// "style" is vital here
});
svg.setAttribute('height', '24');
// Append the SVG to the button
button.appendChild(svg);
// Append the button to the desired parent element
buttonGroup.appendChild(button);
// add an event listener so Tooltips works
button.addEventListener('mouseenter', showTooltip);
button.addEventListener('mouseleave', hideTooltip);
// Container for the + and - buttons, aligned vertically
const buttonsContainer = document.createElement('div');
buttonsContainer.style.display = 'flex';
buttonsContainer.style.flexDirection = 'column';
buttonsContainer.style.alignItems = 'center';
// Create the "+" button with specified styles for background and text color
const inputPlus = document.createElement('button');
inputPlus.innerHTML = "+";
inputPlus.style.cssText = 'height: 12px; width: 12px; background-color: rgb(0,0,128); color: white; border: none; cursor: pointer;';
inputPlus.id = `parameterIncrementButton${index}`;
// add an event listener so Tooltips works
inputPlus.addEventListener('mouseenter', showTooltip);
inputPlus.addEventListener('mouseleave', hideTooltip);
// Create the "-" button with specified styles for background and text color
const inputMinus = document.createElement('button');
inputMinus.innerHTML = "-";
inputMinus.style.cssText = 'height: 12px; width: 12px; background-color: rgb(0,0,128); color: white; border: none; cursor: pointer;';
inputMinus.id = `parameterDecrementButton${index}`;
// add an event listener so Tooltips works
inputMinus.addEventListener('mouseenter', showTooltip);
inputMinus.addEventListener('mouseleave', hideTooltip);
// Event listeners for "+" and "-" buttons to submit the form
inputPlus.addEventListener('click', function() {
submitFormWithAction('parameterplus' + index);
});
inputMinus.addEventListener('click', function() {
submitFormWithAction('parameterminus' + index);
});
// Append + and - buttons to their container
buttonsContainer.appendChild(inputPlus);
buttonsContainer.appendChild(inputMinus);
// Append elements to the button group
buttonGroup.appendChild(buttonsContainer); // Container with + and - buttons
// Append the button group to the target container
document.getElementById('toolbar').appendChild(buttonGroup);
});
});
function submitFormWithAction(actionValue) {
const form = document.getElementById('myForm');
const actionInput = document.getElementById('formAction');
actionInput.value = actionValue;
form.submit();
}
let svgContainer = document.getElementById('svgContainer');
let selectionRectangle = document.createElement('div');
selectionRectangle.id = 'selectionRectangle';
selectionRectangle.style.position = 'absolute';
selectionRectangle.style.border = '1px solid blue';
selectionRectangle.style.pointerEvents = 'none'; // Ignore pointer events
// svgContainer.appendChild(selectionRectangle);
// This is now done on mousedown, when selection starts. If it's done here,
// it makes a visible black dot below the graph and creates scroll bars.
let selectedX = 0;
selectionRectangle.style.left = 0+"px";
let selectedY = 0;
selectionRectangle.style.top = 0+"px";
let selectedWidth = parseInt(selectionRectangle.style.width,10);
let selectedHeight = parseInt(selectionRectangle.style.height,10);
let startX = 0;
let startY = 0;
let initialX = 0;
let initialY = 0;
let isScrolling = false;
let isSelecting = false;
let activeGraphBounds = null;
let whichgraph = 0;
const svg0 = document.getElementById('graph0');
function startDrag(e) {
if (!isScrolling)
return;
e.preventDefault();
startX = e.clientX || e.touches[0].clientX;
tartY = e.clientY || e.touches[0].clientY;
initialX = parseFloat(svg0.style.left) || 0;
initialY = parseFloat(svg0.style.top) || 0;
console.log("start drag", initialX, initialY);
}
function drag(e) {
if (!isScrolling)
return;
e.preventDefault();
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
svg0.style.left = `${initialX + deltaX}px`;
svg0.style.top = `${initialY + deltaY}px`;
console.log(svg0.style.left,svg0.style.position);
}
function endDrag(e) {
if (!isScrolling)
return;
e.preventDefault();
isScrolling = false;
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', endDrag);
console.log("end drag");
}
// the svgContainer will receive mouse events, and
// will handle the selected rectangle and also the
// scrolling of graphs.
// The scrollable elements are the <g> groups that contain each
// graph; but if two graphs (or more) are drawn on the same axes, they both (or all)
// must scroll, which I could only arrange by having svgContainer process s
// the mouse messages
let firstGraphIndex = -1
svgContainer.addEventListener('mousedown', function(e) {
selectionMade = false;
if (mouseFunction == mousePointSlope)
{
pointSlopeOn = true;
handleMouseMove(e);
return;
}
if (!svgContainer.contains(selectionRectangle))
svgContainer.appendChild(selectionRectangle);
let graphs = document.querySelectorAll('.graph');
let rect = svgContainer.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
let k = 0;
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);
console.log("in mousedown: ", graphX, graphY, graphWidth, graphHeight,mouseFunction,x,y);
if (x >= graphX && x <= graphX + graphWidth && y >= graphY && y <= graphY + graphHeight)
{ startX = x;
startY = y;
if(firstGraphIndex == -1)
firstGraphIndex = k; // this will be used on mouseup to know which graph to scroll
if(mouseFunction == mouseSelectRectangle || mouseFunction == mouseSelectCenteredRectangle)
{ isSelecting = true;
console.log("set isSelecting to true because svgContainer got mousedown");
activeGraphBounds = { left: graphX, top: graphY, right: graphX + graphWidth, bottom: graphY + graphHeight };
updateSelectionRectangle(x, y, 0, 0);
break;
}
else if(mouseFunction == mouseScrollGraph)
{
isScrolling = true;
const transform = `translate(0,0)`;
elmnt = graph.querySelector('g.scrollable'); // the scrollable <g> of the graph
elmnt.setAttribute('transform', transform);
isSelecting = false; // prevent selection rectangle
console.log("set isSelecting to false");
// do not break out of the loop.
// do this to all the graphs containing the mouse point,
// not just the first one.
}
}
k=k+1;
}
});
function handleMouseMove(e){
if ((mouseFunction == mouseSelectRectangle || mouseFunction == mouseSelectCenteredRectangle) && ( !isSelecting || !activeGraphBounds) )
return;
if(mouseFunction == mouseScrollGraph && !isScrolling)
return;
e.preventDefault(); // Prevents default action (e.g., text selection)
e.stopPropagation(); // Prevents event from bubbling up to parent elements
let graphs = document.querySelectorAll('.graph');
let rect = svgContainer.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
if (isSelecting && (mouseFunction == mouseSelectRectangle || mouseFunction == mouseSelectCenteredRectangle))
{
// Constrain the selection or the scroll within the manually defined bounds
x = Math.max(activeGraphBounds.left, Math.min(x, activeGraphBounds.right));
y = Math.max(activeGraphBounds.top, Math.min(y, activeGraphBounds.bottom));
let deltax = x - startX;
let deltay = y - startY;
let width = Math.abs(deltax);
let height = Math.abs(deltay);
if(mouseFunction == mouseSelectCenteredRectangle)
{
width *= 2;
height *= 2;
}
let newX = startX < x ? startX : x;
let newY = startY < y ? startY : y;
// Adjust width and height to not exceed the active graph bounds
if (newX + width > activeGraphBounds.right)
width = activeGraphBounds.right - newX;
if (newY + height > activeGraphBounds.bottom)
height = activeGraphBounds.bottom - newY;
if (mouseFunction != mouseScrollGraph)
updateSelectionRectangle(newX, newY, width, height);
}
else if(mouseFunction == mouseScrollGraph && isScrolling)
{
// scroll all the graphs that contain (e.clientX, e.clientY)
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);
console.log(graphX, graphY, graphWidth, graphHeight);
if (x >= graphX && x <= graphX + graphWidth && y >= graphY && y <= graphY + graphHeight)
{
var dx = e.clientX -rect.left - startX;
var dy = e.clientY -rect.top - startY;
const transform = `translate(${dx}, ${dy})`;
elmnt = graph.querySelector('g.scrollable'); // the scrollable <g> of the graph
elmnt.setAttribute('transform', transform);
}
}
}
else if(mouseFunction == mousePointSlope && pointSlopeOn)
{ // Initialize the parameters and data string
var param = `[${x},${y}]`;
var data = `askPointSlopeParam=${encodeURIComponent(param)}`;
// Create the XMLHttpRequest object and set up the request
var xhr = new XMLHttpRequest();
xhr.open("POST", "<?php echo ($_SERVER['PHP_SELF']); ?>", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// Set up an event listener to handle the response
xhr.onreadystatechange = function() {
if (xhr.readyState === 4)
{
if (xhr.status === 200)
{
pointSlopeResponse = xhr.responseText;
console.log('Response received:', pointSlopeResponse);
// pointSlopeResponse consists of two parts: [px,py] followed by PointSlopeSVG
const marker = pointSlopeResponse.indexOf(']') + 1; // start of the SVG
let coordinates = pointSlopeResponse.substring(0,marker);
pointSlopeSVG = pointSlopeResponse.substring(marker);
// Now get this SVG displayed in the PointSlopeElementDiv
pointSlopeElement = document.getElementById("PointSlopeDiv");
pointSlopeElement.style.display = "block";
pointSlopeElement.style.backgroundColor = "lightblue";
pointSlopeElement.style.borderColor = "darkblue";
pointSlopeElement.innerHTML = pointSlopeSVG;
// Wait for the SVG to be inserted into the DOM and set width and height
requestAnimationFrame(() => {
const svgElement = pointSlopeElement.querySelector('svg');
if (svgElement) {
const bbox = svgElement.getBBox();
let newwidth = bbox.width + 10;
let newheight = bbox.height; // doesn't look good larger
pointSlopeElement.style.width = `${newwidth}px`;
pointSlopeElement.style.height = `${newheight}px`;
}
});
// Now extract the coordinates px,py as numbers
console.log(coordinates);
const [px,py] = coordinates.slice(1, -1).split(',').map(Number);
// Now move the crosshairs there
const container = document.getElementById('crosshairs-container');
container.style.left = `${px - 19}px`;
container.style.top = `${py - 19}px`;
container.style.display = "block"; // make it visible
console.log(container);
}
else
{
console.error('Error with request, status code:', xhr.status);
console.error('Response received:', xhr.responseText);
}
}
}
// Send the AJAX message notifying the Engine
console.log('Data being sent:', data);
xhr.send(data);
}
}
svgContainer.addEventListener('mousemove', handleMouseMove);
svgContainer.addEventListener('mouseup', function(e) {
if(mouseFunction == mousePointSlope)
{
pointSlopeOn = false;
}
if(mouseFunction == mouseScrollGraph)
{
isScrolling = false;
// get the element to scroll
elmnt = document.getElementById("scrollable"+firstGraphIndex);
// How much did it scroll?
let rect = svgContainer.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
let deltax = x - startX;
let deltay = y - startY;
if(deltax == 0 && deltay == 0)
return; // don't bother telling the Engine nothing happened
// Next we will submit a form that, when processed, will
// send a graphMoved message to the Engine
// Form the parameter in the required format
var param = `[${firstGraphIndex},${deltax},${deltay}]`;
// 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();
}
if (!isSelecting)
return;
// So now we have selected a rectangle.
isSelecting = false;
activeGraphBounds = null; // Reset the active graph bounds
// The final selected rectangle is available here
selectedX = selectionRectangle.style.left;
selectedY = selectionRectangle.style.top;
selectedWidth = selectionRectangle.style.width;
selectedHeight = selectionRectangle.style.height;
selectionMade = true;
console.log("on mouse up, whichgraph and selected rectangle:");
console.log(whichgraph,selectedX,selectedY,selectedWidth,selectedHeight);
isScrolling = false;
// When the Draw button is clicked, the selected rectangle
// will be submitted in a form, and when that form is processed,
// a selectedRectangleGraph message will be sent to the Engine
// with the rectangle as parameter.
});
function updateSelectionRectangle(x, y, width, height) {
selectionRectangle.style.left = x + 'px';
selectionRectangle.style.top = y + 'px';
selectionRectangle.style.width = width + 'px';
selectionRectangle.style.height = height + 'px';
}
/*______________________________________________________________*/
/* script for resizing the window */
/* We don't want to contact the server until the resize operation
is completed. But that is not an event. So instead, we just wait
300 milliseconds after a resize event to contact the server
*/
let resizeTimer;
function onResizeEnd()
{ console.log("in onResizeEnd");
const newWidth = window.innerWidth
const newHeight = window.innerHeight;
toolbarwidth = <?php echo($toolbarwidth); ?>;
graphwidth = newWidth - toolbarwidth;
if(graphwidth <= 0)
return; // just ignore this event
let param = `[${graphwidth},${newHeight}]`;
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 = 'graphWindowResizedField';
input.value = param;
// Append the input to the form
form.appendChild(input);
// we also need the new width and height
// to be stored in $_SESSION variables so they will
// be available to set the size of svgContainer after Engine
// returns an answer to the graphWindowResized message
// To get them back to PHP they have to be attachd to this form
// Create hidden inputs to hold the new width and height
let input1 = document.createElement('input');
let input2 = document.createElement('input');
input1.type = 'hidden';
input1.name = 'graphwidth';
input1.value = graphwidth;
form.appendChild(input1);
input2.type = 'hidden';
input2.name = 'newHeight';
input2.value = newHeight;
form.appendChild(input2);
// Append the form to the body
document.body.appendChild(form);
// Submit the form
form.submit();
}
window.addEventListener('resize', () =>
{
// Clear the previous timer
clearTimeout(resizeTimer);
// Set a new timer
resizeTimer = setTimeout(() =>
{
console.log('Resize ended');
onResizeEnd();
}, 300); // Adjust the timeout value as needed
});
</script>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists