Sindbad~EG File Manager

Current Path : /home/beeson/public_html/WebMathXpert/Demos/ProblemLibraryDemo/
Upload File :
Current File : //home/beeson/public_html/WebMathXpert/Demos/ProblemLibraryDemo/GraphDoc.php

<?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