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();
}
$nextpagegraph = "https://localhost:8443/Demos/ProblemLibraryDemo/GraphDoc.php";
require("SendMessage.php");
$clientSocket = createClientSocket($serverAddress, $serverPort, $timeout);
// Handle any Ajax requests before emitting any HTML
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['suppliedArgText'])) {
$inputText = $_POST['suppliedArgText'];
$needsArgCondition = $_POST['needsArgCondition'];
// Process the AJAX request
$param = $needsArgCondition . "+" . $inputText;
$response = sendMessage($clientSocket, "checkArg", $param);
} else if (isset($_POST['windowWidth']) && isset($_POST['windowHeight'])) {
$windowWidth = $_POST['windowWidth'];
$windowHeight = $_POST['windowHeight'];
$reasonstart = $_POST['reasonstart'];
$param = $windowWidth . "+" . $windowHeight . "+" . $reasonstart;
$response = sendMessage($clientSocket, "symbolWindowResized", $param);
} else {
$response = "nothing";
}
if ($response === false) {
$errcode = socket_last_error($socket);
$message = socket_strerror($errcode);
echo "Socket_read error: $message<br>";
} else if ($response != "nothing") {
echo($response); // for debugging
exit; // Stop further processing, so the rest of the HTML isn't sent
}
}
// Normal page loading code continues below
?>
<!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 Symbolic Calculator</title>
<style>
/* Set a global font-family rule for all text elements. Only Times New Roman actually works well. */
text {
font-family: 'Times New Roman';
}
svg text {
font-family: 'Times New Roman'; /* Without this, the above rule sometimes doesn't apply to SVG text */
}
.hidden {
visibility: hidden;
position: absolute;
}
#svgContainer {
position: relative;
}
#selectionRectangle {
position: absolute;
border: 1px solid red;
pointer-events: none; /* Ignore this element during mouse events */
}
body {
background-color: CornSilk;
}
.button-container {
display: flex;
gap: 5px; /* Adjust the gap as needed */
align-items: center; /* Align items vertically in the center */
}
.button-container button {
height: 17px; /* Adjust the height as needed */
}
.selectMenuItemWrapper {
display: inline-block;
cursor: pointer;
position: relative;
}
.selectMenuItemWrapper:hover {
background-color: rgb(200,200,255);
}
.highlighted {
background-color: rgb(200,255,200);
}
#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;
}
#modalContent {
background: rgb(200,200,255);
padding: 20px;
border-radius: 5px;
width: 50%;
height: 30%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
#hintModalContent {
background: rgb(200,200,255);
padding: 20px;
border-radius: 5px;
width: 50%;
height: 30%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
#selectionMenu {
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
width: 100px;
z-index: 1;
}
</style>
</head>
<body>
<?php
ob_start();
include 'toolbar.php';
$toolbarContent = ob_get_clean();
?>
<div id="hiddenToolbar" class="hidden">
<?php echo $toolbarContent; ?>
</div>
</body>
</html>
<style>
.hidden {
visibility: hidden;
position: absolute;
}
</style>
<!-- promptModal is used by any dialog. The prompt for the dialog should be dynamically inserted.
Until needed this div has display: none -->
<div id="promptModal" style="display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5);">
<div id="modalContent" style="background: white; margin: 15% auto; padding: 20px; width: 50%;">
<!-- SVG will be inserted here -->
<button id="submitResponse">Submit</button>
</div>
</div>
<div class="button-container" style="display: flex; align-items: center;">
<!-- Wrapper for select elements to stack them vertically -->
<div style="display: flex; flex-direction: column; margin-right: 0px;">
<select id="typesize">
<option value="1">Change Type Size</option>
<option value="2">Normal</option>
<option value="3">Larger</option>
<option value="4">Smaller</option>
</select>
<select id="language">
<option value="-1">Select Language</option>
<option value="0">English</option>
<option value="5">Nederlands</option> <!-- Dutch in Dutch -->
<option value="1">Français</option> <!-- French in French -->
<option value="2">Deutsch</option> <!-- German in German -->
<option value="7">Italiano</option> <!-- Italian in Italian -->
<option value="3">Español</option> <!-- Spanish in Spanish -->
<option value="11">中文</option> <!-- Chinese in Chinese>-->
</select>
</div>
<!-- Other elements remain in the horizontal flow -->
<button onclick="showAssumptions()">Assumptions</button>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display: flex; align-items: center;">
<input type="image" name="autoStep" src="autostep.bmp" Alt="AutoStep" width="60" height="36">
<input type="image" name="autoFinish" src="autofinish.bmp" alt="AutoFinish" width="65" height="36">
<input type="image" name="showStep" src="showstep.bmp" Alt="ShowStep" width="65" height="36">
<input type="image" name="undo" src="undo.bmp" alt="Undo" width="60" height="36">
<input type="image" name="hint" src="hint.bmp" alt="Hint" width="60" height="36">
<input type="image" name="finished" src="finished.bmp" alt="Finished?" width="60" height="36">
</form>
<form method="post" action="<?php echo htmlspecialchars($nextpagegraph); ?>" target="_blank" id="graphForm" style="display:flex; align-items:center;">
<input type="image" name="graphButton" src="pigraph.bmp" alt="Graph" width="60" height="36">
<!-- Correct the input tags and set initial values -->
<input type="hidden" name="widthField2" id="widthField2" value="0">
<input type="hidden" name="heightField2" id="heightField2" value="0">
<input type="hidden" name="toolbarwidthField" id="toolbarwidthField" value="0">
</form>
<script>
// Function to update width and height inputs and submit form
function updateDimensionsAndSubmit() {
// Get width and height of the window in CSS pixels
var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const hiddenToolbar = document.getElementById('hiddenToolbar');
console.log(hiddenToolbar);
const toolbarRect = hiddenToolbar.getBoundingClientRect();
console.log(toolbarRect);
const toolbarwidth = toolbarRect.width;
// Set values to the hidden inputs
hiddenToolbar.value = toolbarwidth;
document.getElementById('widthField2').value = width-toolbarwidth;
document.getElementById('heightField2').value = height;
document.getElementById('toolbarwidthField').value = toolbarwidth;
// Submit the form
document.getElementById('graphForm').submit();
}
// Attach the function to the input image click event
document.querySelector('input[type="image"][name="graphButton"]').addEventListener('click', function(event) {
// Prevent the form from submitting immediately
event.preventDefault();
// Update dimensions and submit the form
updateDimensionsAndSubmit();
});
// Function to set the height of the svgContainer to the scrollHeight of the window,
// and also the height of the element of class "highlight" that contains the selected rectangle
function setSvgContainerHeight() {
const svgContainer = document.getElementById('svgContainer');
// Get the scroll height of the svgContainer (which is at least the total height of its contents)
const containerScrollHeight = svgContainer.scrollHeight;
const highlightSvg = document.querySelector('svg.highlight');
if (highlightSvg) {
// Set the height using both attribute and style
highlightSvg.setAttribute("height", containerScrollHeight);
highlightSvg.style.height = containerScrollHeight + "px"; // Adding 'px' to specify the unit
}
svgContainer.style.height = containerScrollHeight + 'px';
// Scroll the container to the bottom
svgContainer.scrollTop = svgContainer.scrollHeight;
}
// Set the height on page load
window.addEventListener('load', setSvgContainerHeight);
// Update the height if the window is resized
window.addEventListener('resize', setSvgContainerHeight);
// Scroll the svgContainer to the bottom whenever new content is loaded
window.addEventListener('load', () => {
const svgContainer = document.getElementById('svgContainer');
svgContainer.scrollTop = svgContainer.scrollHeight;
});
</script>
<script>
// This function is not actually used, since resizing is handled locally and the Engine
// is just notified of the change by an Ajax message. But if this form is used, the
// Engine can handle resizing, which will be needed if in the future line breaks are implemented.
// It does, however, inevitably create flicker
function submitResizeForm(width, height) {
// Create a form element
const form = document.createElement('form');
form.method = 'POST';
form.action = '<?php echo $_SERVER["PHP_SELF"]; ?>';
// Create hidden input elements for width and height
const widthInput = document.createElement('input');
widthInput.type = 'hidden';
widthInput.name = 'windowWidth';
widthInput.value = width;
const heightInput = document.createElement('input');
heightInput.type = 'hidden';
heightInput.name = 'windowHeight';
heightInput.value = height;
// Append inputs to the form
form.appendChild(widthInput);
form.appendChild(heightInput);
// Append form to the body
document.body.appendChild(form);
// Submit the form
form.submit();
}
window.addEventListener('resize', function() {
const width = window.innerWidth;
const height = window.innerHeight;
let reasonstart = 0.7 * width; // keep this in sync with the original setting
// Select all elements with the class 'reason'
const reasons = document.querySelectorAll('.reason');
// Update the .left attribute of each reason element
reasons.forEach(reason => {
reason.style.left = reasonstart + 'px';
});
// 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");
const data = `windowWidth=${encodeURIComponent(width)}&windowHeight=${encodeURIComponent(height)}&reasonstart=${encodeURIComponent(reasonstart)}`;
console.log('Data being sent:', data);
xhr.send(data);
});
</script>
</div>
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST")
{ if (isset($_POST['autoProblemField']))
{
$problemnumber = $_POST["autoProblemField"];
$topicnumber = $_POST["autoTopicField"];
$sender = "autoTest";
$language = $_POST["language"];
$width = $_POST["width"];
$_SESSION['languagenumber'] = $language;
}
if (isset($_POST['problemnumberField']))
{
$problemnumber = $_POST["problemnumberField"];
$topicnumber = $_POST["topicField"];
$sender = "GetProblem";
$language = $_POST["language"];
$width = $_POST["widthField"];
$height = $_POST["heightField"];
$_SESSION['languagenumber'] = $language;
$windowWidth = $_SESSION['width'] = intval($_POST["widthField"]); // width of the browser window
$toolbarwidth = $_SESSION['toolbarwidth'] = intval($_POST["toolbarwidthField"]);
}
if (isset($_POST['autoStep_x']))
{
$sender = "autoStep";
}
if (isset($_POST['showStep_x']))
{
$sender = "showStep";
}
if (isset($_POST['autoFinish_x']))
{
$sender = "autoFinish";
}
if (isset($_POST['undo_x']))
{
$sender = "undo";
}
if (isset($_POST['hint_x']))
{
$sender = "hint";
}
if (isset($_POST['finished_x']))
{
$sender = "finished";
}
if (isset($_POST['language2']))
{
$sender = "setLanguage";
$language = $_POST['language2'];
}
if (isset($_POST['commandID']))
{
$commandID = $_POST['commandID'];
$_SESSION['commandID'] = $commandID;
// so we can find it when we are ready to exec it
$sender = "selectMenuChoice";
}
if (isset($_POST['selectedRectangleField']))
{
$sender = "selectedRectangleSymbol";
$param = $_POST['selectedRectangleField'];
}
if (isset($_POST['suppliedArgText']))
{
$sender = "suppliedArgText";
$param = $_POST['suppliedArgText'];
}
if (isset($_POST['arg'])) // this is the arg text that has parsed and passed check_arg
{
$sender = "execOpWithArg";
$param = $_SESSION['commandID'] . "+" . $_POST['arg'];
}
$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 ($result)
{ if($sender=="GetProblem")
{
$reasonstart = intval(0.7 * $width); // x-coordinate where we want the reasons to start, in papyrus coordinates
$response = sendMessage($clientSocket,"initSymbolDocFromLibrary",
$topicnumber . "+" . $problemnumber . "+" . $reasonstart . "+" . $width. "+" . "$language");
if ($response === false)
{
$errcode = socket_last_error($socket);
$message = socket_strerror($errcode);
echo "Socket_read error: $message<br>";
}
else
{
// The response consists of two <svg> elements, one for the problem, and one for the reason, in this case "the problem"
?>
<div id="svgContainer">
<?php
list($line0, $reason) = explode("</svg>\n<svg", $response);
$line0 = $line0 . "</svg>\n";
$reason = "<svg" . $reason;
echo($line0);
echo($reason);
?>
</div>
<?php
}
socket_close($socket); // because server has already closed it
}
else if($sender=="autoTest")
{ $breakwidth = $width; // max length of pieces of broken lines
$reasonstart = intval(0.7 * $width); // x-coordinate where we want the reasons to start, in papyrus coordinates 2
$param = $topicnumber . "+" . $problemnumber . "+" . $reasonstart . "+" . $breakwidth. "+" . "$language";
$response = sendMessage($clientSocket,"initAndAutoFinish",$param);
if ($response === false)
{
$errcode = socket_last_error($socket);
$message = socket_strerror($errcode);
echo "Socket_read error: $message<br>";
}
else
{
?>
<div id="svgContainer">
<?php
echo($response);
?>
</div>
<?php
}
socket_close($socket); // because server has already closed it
}
else if($sender == "autoStep" or $sender == "undo" or $sender == "autoFinish" or $sender == "selectMenuChoice" or $sender == "execOpWithArg" or
$sender == "showStep" or $sender == "finished" or $sender == "hint" or $sender == "setLanguage" or $sender == "selectedRectangleSymbol" or
$sender == "symbolWindowResized"
)
{
if($sender == "setLanguage")
{
$param = $language;
$_SESSION['languagenumber'] = $language;
}
else if($sender == "selectMenuChoice")
{
$param = $commandID;
}
else if(! isset($param))
$param = "dummy";
$response = sendMessage($clientSocket,$sender,$param);
if ($response === false)
{
$errcode = socket_last_error($socket);
$message = socket_strerror($errcode);
echo "Socket_read error: $message<br>";
}
else
{
// The response consists of many <svg> elements, to redraw the whole computation
// it may also include other things to be echoed, such as "<script> highlightIndex = 3; </script>"
?>
<div id="svgContainer">
<?php
echo($response);
?>
</div>
<?php
}
socket_close($socket); // because server has already closed it
}
}
else
{
echo "Failed to connect to the C program: " . socket_strerror(socket_last_error()) . "<br>";
}
}
}
?>
<div id="hintModal">
<div id="hintModalContent">
<button onclick="hideHintModal()">Close</button>
</div>
</div>
<script src="crashPopup.js"></script>
<script>
window.addEventListener('beforeunload', function (e) {
// Send the beacon request to notify the server about session end
navigator.sendBeacon('<?php echo $_SERVER["PHP_SELF"]; ?>?action=endSession');
});
</script>
<script>
<?php
if($sender == "hint")
{ // embed JavaScript to show the hint modal on page load
echo 'document.addEventListener("DOMContentLoaded", function() {
// Show the hint modal
showHintModal();
});';
}
if($sender == "selectedRectangleSymbol" || $sender == "showStep")
{
echo 'document.addEventListener("DOMContentLoaded", function() {
console.log("about to display selection menu");
showSelectionMenu(200,80);
});';
}
?>
document.getElementById('typesize').addEventListener('change', function() {
var selectedValue = this.value;
// Immediately invoke the appropriate function based on the selected value
if(selectedValue === "3") {
LargerType();
} else if(selectedValue === "4") {
SmallerType();
} else if(selectedValue === "2") {
NormalType();
}
// Use a timeout to reset the select box, allowing the UI to update
// This permits the user to choose "Larger" twice in a row.
setTimeout(() => {
this.selectedIndex = 0; // Resets to a non-selected state
}, 100); // Short delay to ensure the UI has time to react
});
document.getElementById('language').addEventListener('change', function() {
var language = this.value;
setLanguage(language);
});
// Initialize the default scale factor
var scaleFactor = 1;
function AutoStep(){
// submit a hidden form, so that PHP can process it and send the autoStep message
// the form needs no input fields.
var myform = document.createElement('form');
// Set form attributes
myform.setAttribute('method', 'post');
myform.setAttribute('action', <?php echo json_encode($_SERVER['PHP_SELF']); ?>); // Use json_encode to properly handle strings
myform.style.display = 'none';
// Append the form to the body
document.body.appendChild(myform);
// Submit the form
myform.submit();
}
function Graph(){
// submit a hidden form, so that PHP can process it and send the Graph message
// the form needs no input fields.
var myform = document.createElement('form');
// Set form attributes
myform.setAttribute('method', 'post');
myform.setAttribute('action', <?php echo json_encode($nextpagegraph); ?>);
myform.style.display = 'none';
// Append the form to the body
document.body.appendChild(myform);
// Submit the form
myform.submit();
}
function setLanguage(){
// Assume 'language' is the id of the language select dropdown
var languageValue = document.getElementById('language').value;
var myform = document.createElement('form');
// Set form attributes
myform.setAttribute('method', 'post');
myform.setAttribute('action', <?php echo json_encode($_SERVER['PHP_SELF']); ?>); // Use json_encode to properly handle strings
myform.style.display = 'none';
// Create a hidden input to carry the language value
var languageInput = document.createElement('input');
languageInput.setAttribute('type', 'hidden');
languageInput.setAttribute('name', 'language2');
languageInput.setAttribute('value', languageValue);
// Append the hidden input to the form
myform.appendChild(languageInput);
// Append the form to the body
document.body.appendChild(myform);
// Submit the form
myform.submit();
}
function selectedRect(param)
// called on mouseup, with param set to be the parameter for the selectedRectangleSymbol message
{
var myform = document.createElement('form');
// Set form attributes
myform.setAttribute('method', 'post');
myform.setAttribute('action', <?php echo json_encode($_SERVER['PHP_SELF']); ?>); // Use json_encode to properly handle strings
myform.style.display = 'none';
// Create a hidden input to carry the selected rectangle
var selectedRectangleInput = document.createElement('input');
selectedRectangleInput.setAttribute('type', 'hidden');
selectedRectangleInput.setAttribute('name', 'selectedRectangleField');
selectedRectangleInput.setAttribute('value', param);
// Append the hidden input to the form
myform.appendChild(selectedRectangleInput);
// Append the form to the body
document.body.appendChild(myform);
// Submit the form
myform.submit();
}
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 to show the hint modal
function showHintModal() {
// Get the hint modal
var hintModal = document.getElementById('hintModal');
hintModal.style.display = 'flex';
console.log("called showHintModal");
// Create a div with relative positioning
var div = document.createElement('div');
div.style.position = 'relative';
// Append the div to the modal content
var modalContent = document.getElementById('hintModalContent');
modalContent.appendChild(div);
// Display all SVG elements with class "hint" (There will only be one)
var hints = document.querySelectorAll('.hint');
hints.forEach(function (hint) {
// Clone the hint
var clonedHint = hint.cloneNode(true);
// Set the display property to block (or any other appropriate value)
clonedHint.style.display = 'block';
// Append the cloned hint to the div
div.appendChild(clonedHint);
});
}
// Function to hide the hint modal
function hideHintModal() {
var hintModal = document.getElementById('hintModal');
hintModal.style.display = 'none';
}
function SmallerType() {
// Check if it's already at the minimum size
if (scaleFactor > 0.7) {
// Decrease the scale factor
var oldscaleFactor = scaleFactor;
scaleFactor -= 0.15;
// Get the container that holds the SVG elements
var container = document.getElementById('svgContainer');
// Loop through all SVG child elements of the container
for (var i = 0; i < container.children.length; i++) {
var svgElement = container.children[i]; // Get the current SVG element
if (svgElement.tagName.toLowerCase() === 'svg') {
// Get the current width and height from the SVG element
var currentWidth = parseFloat(svgElement.getAttribute('width'));
var currentHeight = parseFloat(svgElement.getAttribute('height'));
// Calculate the new width and height based on the scale factor
var newWidth = currentWidth * (scaleFactor/oldscaleFactor);
var newHeight = currentHeight * (scaleFactor/oldscaleFactor);
// Set the new width and height attributes of the SVG element
svgElement.setAttribute('width', newWidth.toString());
svgElement.setAttribute('height', newHeight.toString());
// the element's viewBox is not changed.
}
}
}
}
function LargerType() {
// Check if it's already at the maximum size
if (scaleFactor < 1.5 ) {
var oldscaleFactor = scaleFactor;
// Increase the scale factor by
scaleFactor += 0.15;
// Get the container that holds the SVG elements
var container = document.getElementById('svgContainer');
// Loop through all SVG child elements of the container
for (var i = 0; i < container.children.length; i++) {
var svgElement = container.children[i]; // Get the current SVG element
if (svgElement.tagName.toLowerCase() === 'svg') {
// Get the current width and height from the SVG element
var currentWidth = parseFloat(svgElement.getAttribute('width'));
var currentHeight = parseFloat(svgElement.getAttribute('height'));
// Calculate the new width and height based on the scale factor
var newWidth = currentWidth * (scaleFactor/oldscaleFactor);
var newHeight = currentHeight * (scaleFactor/oldscaleFactor);
// Set the new width and height attributes of the SVG element
svgElement.setAttribute('width', newWidth.toString());
svgElement.setAttribute('height', newHeight.toString());
// the element's viewBox is not changed.
}
}
}
}
function NormalType() {
var oldscaleFactor = scaleFactor;
scaleFactor = 1.0;
// Get the container that holds the SVG elements
var container = document.getElementById('svgContainer');
// Loop through all SVG child elements of the container
for (var i = 0; i < container.children.length; i++) {
var svgElement = container.children[i]; // Get the current SVG element
if (svgElement.tagName.toLowerCase() === 'svg') {
// Get the current width and height from the SVG element
var currentWidth = parseFloat(svgElement.getAttribute('width'));
var currentHeight = parseFloat(svgElement.getAttribute('height'));
// Calculate the new width and height based on the scale factor
var newWidth = currentWidth * (scaleFactor/oldscaleFactor);
var newHeight = currentHeight * (scaleFactor/oldscaleFactor);
// Set the new width and height attributes of the SVG element
svgElement.setAttribute('width', newWidth.toString());
svgElement.setAttribute('height', newHeight.toString());
// the element's viewBox is not changed.
}
}
}
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
let selectedX = selectionRectangle.style.left = 0;
let selectedY = selectionRectangle.style.top = 0;
let selectedWidth = selectionRectangle.style.width;
let selectedHeight = selectionRectangle.style.height;
let windowwidth = document.body.clientWidth;
let windowheight = window.innerHeight; // technically this is the "viewport height"
let startX, startY, isSelecting = false;
let activeGraphBounds = null;
let whichline = 0;
// the following variables are set in mousedown and used in mouseup
let lineX = 0;
let lineY = 0;
let lineWidth = 0;
let lineHeight = 0;
let activeline = 0;
function currentlinenumber() {
const lines = document.querySelectorAll('.line'); // Get all elements with class "line"
let maxId = -1; // Start with -1 so even if no lines are found, the result is -1
lines.forEach(line => {
const id = parseInt(line.getAttribute('id').replace('line', ''), 10);
if (id > maxId) {
maxId = id;
}
});
return Math.abs(maxId);
}
svgContainer.addEventListener('mousedown', function(e) {
if (e.shiftkey == false && selectionMade)
selectionMade = false;
if (!svgContainer.contains(selectionRectangle))
svgContainer.appendChild(selectionRectangle);
let lines = document.querySelectorAll('.line');
let rect = svgContainer.getBoundingClientRect();
// if there is a selectionMenu already on-screen, and this mousedown is outside it, remove it
const menuContainer = document.getElementById('selectionMenu');
if (menuContainer) {
let menurect = menuContainer.getBoundingClientRect();
// Check if the click is outside of menurect
if (e.clientX < menurect.left || e.clientX > menurect.right ||
e.clientY < menurect.top || e.clientY > menurect.bottom) {
// Click is outside, so remove the menuContainer
document.body.removeChild(menuContainer);
// Remove the rectangles of class "selectedRectangle", which
// color the background of the selected term(s)
document.querySelectorAll('.selectmenuitem').forEach(item => {
item.parentNode.removeChild(item); // Remove all the menu items too
});
document.querySelectorAll('.selectedRectangle').forEach(rect => {
rect.parentNode.removeChild(rect); // Remove each rectangle from its parent node
// And we also remove the svg elements that contain the selected rectangles.
document.querySelectorAll('.highlight').forEach(svg => {
svg.parentNode.removeChild(svg);
});
// This doesn't remove the selectedRectangle from the document in the Engine.
// It will be removed anyway at the next step, so even though there is an Ajax
// messsage to remove it, that's not necessary.
// The selectionRectangle div now appears as a 2px by 2px dot.
// The following code removes it
const selectedRectangle = document.getElementById('selectionRectangle');
selectedRectangle.parentNode.removeChild(selectionRectangle);
});
}
}
let x = e.clientX - rect.left;
let y = e.clientY - rect.top; // Now these coordinates are relative to svgContainer
activeline = currentlinenumber(); // should be active line number, but can't get that from here yet
let lineid = "line" + activeline;
var line = document.getElementById(lineid);
lineX = parseInt(line.style.left,10); // these coordinates are relative to svgContainer
lineY = parseInt(line.style.top,10);
lineWidth = line.width.baseVal.value;
lineHeight = line.height.baseVal.value;
if (x >= lineX && x <= lineX + lineWidth && y >= lineY && y <= lineY + lineHeight)
{
isSelecting = true;
startX = x;
startY = y;
activeGraphBounds = { left: lineX, top: lineY, right: lineX + lineWidth, bottom: lineY + lineHeight };
updateSelectionRectangle(x, y, 0, 0);
}
});
document.addEventListener('mousemove', function(e) {
e.stopPropagation();
e.preventDefault();
if (!isSelecting || !activeGraphBounds) return;
// prevent the browser from getting the mousemoves
// but this doesn't seem to work
let rect = svgContainer.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
// Constrain the selection 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 width = Math.abs(x - startX);
let height = Math.abs(y - startY);
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;
updateSelectionRectangle(newX, newY, width, height);
});
document.addEventListener('mouseup', function(e) {
if (!isSelecting) return;
isSelecting = false;
activeGraphBounds = null; // Reset the active graph bounds
// The final selected rectangle is available here
selectedX = parseInt(selectionRectangle.style.left,10);
selectedY = parseInt(selectionRectangle.style.top,10);
selectedWidth = parseInt(selectionRectangle.style.width,10);
selectedHeight = parseInt(selectionRectangle.style.height,10);
if (!(selectedWidth > 0 && selectedHeight > 0))
return;
selectionMade = true;
// Next create the parameter for the selectedRectangleSymbol method, which is supposed to be
// [left,top,width,height,lineleft,linetop,linewidth,lineheight,windowwidth,windowheight].
param = "[" + [selectedX, selectedY,selectedWidth,selectedHeight,lineX,lineY,lineWidth,lineHeight,windowwidth,windowheight]+ "]"
console.log(param);
selectedRect(param);
});
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';
}
function showSelectionMenu(x, y)
// This shows the selection menu with upper left corner at x,y,
// provided there are any elements of class "selectmenuitem".
// If global variable highlightIndex is nonnegative, highlight the item with that index
{
if(typeof highlightIndex == 'undefined')
highlightIndex = -1; // Maybe it's already been defined in the response to the showStep message
// Find all selectmenuitem SVG elements
const menuItems = document.querySelectorAll('.selectmenuitem');
// Check if menuItems is empty
if (menuItems.length === 0) {
console.log('No menu items found.');
return; // Exit the function early
}
// Create or find the menu container
let menuContainer = document.getElementById('selectionMenu');
if (!menuContainer) {
menuContainer = document.createElement('div');
menuContainer.id = 'selectionMenu';
document.body.appendChild(menuContainer);
}
// Clear previous menu items
menuContainer.innerHTML = '';
// Set position
menuContainer.style.position = 'absolute';
menuContainer.style.left = x + 'px';
menuContainer.style.top = y + 'px';
menuContainer.style.display = 'block';
menuContainer.style.backgroundColor = 'white';
menuContainer.style.border = '1px solid black';
menuContainer.style.padding = '0px';
let menuwidth = 20; // not 20px
let nitems = 0;
let accumulatedHeight = 0; // Keep track of the total height of menu items added so far
const menuItemSpacing = 3; // Spacing between menu items
// calculate maximum width of the menuItems
menuItems.forEach((item, index) => {
const itemWidth = parseInt(item.getAttribute('width'));
if (itemWidth > menuwidth) {
menuwidth = itemWidth;
}
});
// Append items to the container and add click event listeners
menuItems.forEach((item, index) => {
const wrapper = document.createElement('div');
wrapper.className = 'selectMenuItemWrapper';
const menuItem = item.cloneNode(true); // Clone the SVG element
menuItem.style.display = "block"; // Make SVG visible
const itemWidth = parseInt(menuItem.getAttribute('width'));
const itemHeight = parseInt(menuItem.getAttribute('height'));
// Set wrapper styles
wrapper.style.width = `${menuwidth}px`;
wrapper.style.height = `${itemHeight}px`;
wrapper.style.position = 'absolute';
wrapper.style.left = '0px'; // align items to the left
wrapper.style.top = `${accumulatedHeight}px`; // Position items vertically
// Highlight only if highlightIndex is non-negative and matches the current index
if (index === highlightIndex && highlightIndex >= 0) {
wrapper.classList.add('highlighted');
}
// Add the current item's height plus spacing to the total height
accumulatedHeight += itemHeight + menuItemSpacing;
wrapper.appendChild(menuItem); // Wrap the SVG with the div
wrapper.addEventListener('click', function() {
const commandID = parseInt(menuItem.getAttribute('id')); // Use menuItem to get the commandID
console.log(commandID);
selectMenuChoice(commandID);
});
menuContainer.appendChild(wrapper);
});
// Adjust the menu container's width to fit all items
menuContainer.style.width = `${menuwidth}px`;
// Set the menu container's total height to fit all items including spacing
accumulatedHeight -= menuItemSpacing;
menuContainer.style.height = `${accumulatedHeight}px`;
}
function selectMenuChoice(commandID) {
console.log('Selected command ID:', commandID);
// Create and submit a hidden form to carry commandID as $_POST data
const form = document.createElement('form');
form.method = 'POST';
form.action = '<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>'; // Dynamically set to this script
form.style.display = 'none';
// Create a hidden field for commandID
const hiddenField = document.createElement('input');
hiddenField.type = 'hidden';
hiddenField.name = 'commandID';
hiddenField.value = commandID;
// Append the hidden field to the form
form.appendChild(hiddenField);
// Append the form to the body and submit it
document.body.appendChild(form);
form.submit();
}
// Declare c at a higher scope
let conditionC;
document.addEventListener('DOMContentLoaded', function() {
showCrashPopup(); // if a crash has occurred, notify the user.
const svgPrompt = document.querySelector('.needsargprompt');
if (svgPrompt) {
displayPrompt(svgPrompt);
}
});
function displayPrompt(svgElement) {
const modal = document.getElementById('promptModal');
const modalContent = document.getElementById('modalContent');
// Clear previous content
modalContent.innerHTML = '';
// Extract condition from svgElement's ID
const idStr = svgElement.id;
const match = idStr.match(/\d+$/); // Matches number at the end of the string
conditionC = match ? parseInt(match[0], 10) : null; // Save the parsed number globally
// Clone the SVG element, modify its style properties, and append it
const clonedSvg = svgElement.cloneNode(true);
clonedSvg.style.display = 'block'; // Ensure it is visible within the modal
clonedSvg.style.position = 'relative'; // Adjust positioning to be relative within the modal
modalContent.appendChild(clonedSvg);
// Create and append the input field
const inputField = document.createElement('input');
inputField.id = 'responseInput';
inputField.type = 'text';
inputField.placeholder = 'Enter your response here';
modalContent.appendChild(inputField);
// Create a container for the buttons
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.justifyContent = 'space-evenly';
buttonContainer.style.marginTop = '10px'; // Space above the buttons
// Create and append the OK (submit) button
const okButton = document.createElement('button');
okButton.textContent = 'OK';
okButton.addEventListener('click', submitResponse);
buttonContainer.appendChild(okButton);
// Create and append the Cancel button
const cancelButton = document.createElement('button');
cancelButton.textContent = 'Cancel';
cancelButton.addEventListener('click', function() {
document.getElementById('promptModal').style.display = 'none';
});
buttonContainer.appendChild(cancelButton);
// Append the button container to the modal content
modalContent.appendChild(buttonContainer);
// Create and append the response area
const responseArea = document.createElement('div');
responseArea.id = "responseArea";
modalContent.appendChild(responseArea);
// Show the modal
modal.style.display = 'block';
// Ensure the modal and its contents are accessible
modal.style.zIndex = '1000'; // Make sure modal is above other content
modal.style.pointerEvents = 'auto'; // Ensure it can be interacted with
// Focus on input after showing the modal
inputField.focus();
}
function submitResponse() {
const inputText = document.getElementById('responseInput').value;
console.log('Entered text:', inputText);
console.log('conditionC = ',conditionC);
if (inputText.trim().length == 0)
{ alert("You must enter a formula.") // This will have to be localized.
// document.querySelector('.needsargprompt'); is the prompt, as SVG; Let's just repeat that in the alert.
return;
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
const data = `suppliedArgText=${encodeURIComponent(inputText)}&needsArgCondition=${encodeURIComponent(conditionC)}`;
console.log('Data being sent:', data);
xhr.send(data);
// Get the server response
xhr.onload = function() {
if (xhr.status == 200) {
console.log("Response received:", xhr.responseText);
// Did it parse? Did it satisfy check_arg?
let responseText = xhr.responseText;
if(responseText == "0+0") {
// no message: it parsed and passed check_arg
checkArgMessage = "OK";
// Create a hidden form to submit the argument
const form = document.createElement('form');
form.method = 'POST';
form.action = '<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>'; // Adjust the action as needed
form.style.display = 'none';
// Create a hidden input to carry the argument value
const hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'arg';
hiddenInput.value = inputText;
// Append the hidden input to the form
form.appendChild(hiddenInput);
document.body.appendChild(form);
form.submit();
console.log("Form submitted with arg:", inputText);
document.getElementById('promptModal').style.display = 'none'; // close the modal
// There is no real need to close the modal as the whole file is finished when we submit the form,
// but if I don't close it here, there is a brief flashing view of "0+0", which is thus prevented.
}
// Update the modal with the response from the server
const responseArea = document.getElementById('responseArea');
// responseText is an <svg> element, possibly with display:none.
// We need to put it into responseArea after setting display:block
responseArea.innerHTML = responseText;
svgElement = responseArea.querySelector('svg');
if(svgElement) {
svgElement.style.display = 'block'; // Ensure SVG is visible
console.log("SVG element found and made visible");
}
responseArea.style.display = 'block'; // Make sure it's visible
responseArea.style.position = "relative";
if(svgElement.classList.contains("feedback")) {
// conditionC is intbyparts1, so change it to intbyparts2 now
conditionC = conditionC + 1;
console.log("New conditionC is ", conditionC);
}
} else {
console.log("Error in Ajax request");
}
};
// Optionally, one might clear the input
// document.getElementById('responseInput').value = '';
// or close the modal by
// document.getElementById('promptModal').style.display = 'none';
}
</script>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists