Sindbad~EG File Manager

Current Path : /home/beeson/public_html/WebMathXpert/
Upload File :
Current File : //home/beeson/public_html/WebMathXpert/arrowButton.js

function conjuncts(x){
// split x into a list at commas not inside parentheses
// and return the resulting list.

	let parendepth = 0;
	let result = [];
	let currentSegment = '';

	for (let k = 0; k < x.length; k++) {
		if (x[k] === '(') {
			parendepth++;
		} 
		else if (x[k] === ')') {
			parendepth--;
		}

		if (parendepth === 0 && x[k] === ',') {
			// End of a segment
			result.push(currentSegment.trim());
			currentSegment = '';
		} 
		else {
			// Append character to the current segment
			currentSegment += x[k];
		}
 }

 // Push the last segment
 if (currentSegment) {
	 result.push(currentSegment.trim());
 }

 return result;
}

function stripDigitAtEnd(str) {
	// Check if the last character is a digit and if so, remove it
	if (/\d$/.test(str)) {
		return str.slice(0, -1); // Remove the last character
	}
	return str; // Return the original string if no digit is found
}

/*
console.log(stripDigitAtEnd("example1")); // Output: "example"
console.log(stripDigitAtEnd("example"));  // Output: "example"
console.log(stripDigitAtEnd("test123"));  // Output: "test12"
*/

function addNewFields(nproblems,IDroot,NameRoot,list){
	// list is assumed to be a list containing parseable text for nproblems entries,
	let entryFieldsContainer = document.getElementById("EntryFields");
	for (i = 1; i <= nproblems; i += 1) {
		let nextID = IDroot + i;
		if(i==1 && document.getElementById(IDroot))
			nextID =  IDroot;
		let nextInputField = document.getElementById(nextID);
		if (nextInputField == null) {  // the first two will already exist, the rest not
		   // Create the input element
		   nextInputField = document.createElement("input");
		   nextInputField.id = nextID;
		   nextInputField.name = NameRoot + i; // Ensure the name for POST
		   nextInputField.type = "text"; // Specify the input type
			nextInputField.style.display = "none";

		   // Create the wrapper div with the class "function-entry"
		   const wrapperDiv = document.createElement("div");
		   wrapperDiv.className = "function-entry";

		   // Append the input field to the wrapper div
		   wrapperDiv.appendChild(nextInputField);
			nextInputField.value = list[i - 1];  // list indices start at 0, input field id's start at 1
		   // Append the wrapper div to the EntryFields container
		   entryFieldsContainer.appendChild(wrapperDiv);
			console.log("Adding ", nextInputField, entryFieldsContainer.id);
		}
		else{
		// Update the value of the input field  for the first two, that already exist
		nextInputField.value = list[i - 1];
		}
		console.log(nextInputField);
		if(problemtype === 206  // RELATED_RATES
			&& list[i-1].includes("diff") && !list[i].includes("diff")){
				// the rest of the equations are "good for one special time", so we change EntryFields to EntryFields2
				// so the rest of them will be put there, below the label "Equations good for one special time"
				entryFieldsContainer = document.getElementById("EntryFields2");
				console.log("shifting before ", list[i]);
		}
	}
	// update the hidden field 'nequations'
	nequationsField = document.getElementById('nequations');
	if(nequationsField){
		nequationsField.value = nproblems;
	   console.log("setting nequationsField.value to ", nproblems);
	}
}

function setRiemannStyleSelector(valueToSet, containerId = 'EntryFields') {
    let selectElement = document.querySelector('select[name="riemannStyle"]');
    if (!selectElement) {
        console.log("riemannStyle selector not found. Creating it.");

        // Create the <label> element
        const labelElement = document.createElement('label');
        labelElement.id = "riemannStyleLabel";
        labelElement.style.width = "160px";
        labelElement.style.textAlign = "right";
        labelElement.style.marginRight = "10px";
        labelElement.textContent = "Style of rectangles:";

        // Create the <select> element
        selectElement = document.createElement('select');
        selectElement.name = "riemannStyle";
        selectElement.style.width = "100px";

        // Create the <option> elements
        const options = [
            { value: "0", text: "Left" },
            { value: "1", text: "Centered" },
            { value: "2", text: "Right" }
        ];
        options.forEach(optionData => {
            const optionElement = document.createElement('option');
            optionElement.value = optionData.value;
            optionElement.textContent = optionData.text;
            selectElement.appendChild(optionElement);
        });

        // Append the label and select to the DOM
        const container = document.getElementById(containerId) || document.body;
        container.appendChild(labelElement);
        container.appendChild(selectElement);

        console.log("riemannStyle selector created and added to the DOM.");
    }

    // Set the value dynamically
    if (valueToSet !== undefined) {
        selectElement.value = valueToSet;
        // console.log(`riemannStyle value set to: ${valueToSet}`);
    } else {
        console.warn("valueToSet is undefined. Cannot set riemannStyle value.");
    }
}

function presentProblem(newTopic, problemText, inputField, problemtype)
// set the topic,  put the problemText in the correct entry field,
// parse and display the problem
{  let list;
	if(!problemText.includes("|")){ 
		 list = conjuncts(problemText);   // list of formulas to enter in different fields
	 }
	else{  // after "|"  come range inequalities
		// example,  problemText could be 
		//   " (pi *((x+pi)/(2 pi)-floor((x+pi)/(2 pi)))-pi/2, sum( (-1)^(k+1) sin(kx)/k, k, 1,n))  | -12 < x < 12, -2 < y < 2 "
		let list2 = problemText.split("|").map(s => s.trim());
		let problem = list2[0];
		let ranges = conjuncts(list2.slice(1).join(","));  // Convert array back to string
		// and make each range inequality a separate item in the list 
		if (problem.startsWith('(') && problem.endsWith(')')) {
		    problem = problem.slice(1, -1);
		}
	   list = conjuncts(problem);
		list.push(...ranges);  // append ranges to  list
	}
	
	console.log(list);
	nproblems = list.length;  
	let nOriginalInputFields = 2; 
	if(problemtype == 5)  // PARAMETRIC_GRAPH from mainchoi.h
		nOriginalInputFields = 3;  // two functions and a parameter interval 
	if(problemtype == 18)  // RIEMANN_SUMS
		nOriginalInputFields = 5;  // function, from, to, nintervals, direction 
	if(problemtype == 19 || problemtype == 20) // TRAPEZOID_RULE or SIMPSONS_RULE
		nOriginalInputFields = 4;
	// console.log("nOriginalInputFields =",nOriginalInputFields);
	topic = newTopic;
	// But that only sets a Javascript variable; we also need:
	let topicField3 = document.getElementById('topicField3');
	if(topicField3){
		topicField3.value = newTopic;
		// console.log("presentProblem changes topic to ", newTopic);  // you have to block the display button click at the end
	}
	var i;
	if(nproblems == 1){
		// Update the value of the (only) input field
		nOriginalInputFields = 1; 
		inputField.value = list[0];
		if(problemtype == 205){ // IMPLICIT_DIFF
			// there are two input fields in EnterImplicitDiff.php.  The first
			// one has been filled with a problem from the MathXpert Problem Library
			// Those problems always are to solve for dy/dx, so diff(y,x) needs to 
			// go in the second input field.
			secondField = document.getElementById('implicitderivativeID');
			secondField.value = "diff(y,x)";
		} 
		if(problemtype == 9){  // 9 is ODE in mainchoi.h; it's a graph type so we need mainchoice, not problemtype
			// there are two input fields in EnterODE.php, for an equation and 
			// initial conditions.  The problem library supplies only an equation,
			// so we here supply the default conditions (1,1).
			secondField = document.getElementById('shadowInitialConditionsID');
			secondField.value = "(1,1)";
		}
		if(problemtype == 12){  // 12 is HDE in mainchoi.h; it's a graph type so we need mainchoice, not problemtype
			// there are two input fields in EnterODE.php, for an equation and 
			// initial conditions. The problem library supplies only an equation,
			// so we here supply the default conditions (1,2,3).
			nOriginalInputFields = 1; 
			inputField.value = list[0];
			secondField = document.getElementById('shadowInitialConditionsID');
			secondField.value = "(1,2,3)";
			}
	}
	else if (problemtype == 0 ||   // ORDINARY_GRAPH from mainchoi.h, not 18 from probtype.h
				problemtype == 3 ||   //  MC_INEQ
				problemtype == 4 ||  // MC_SET
				problemtype == 13 ||  //  RELATION 	
				problemtype == 8     //  POLYROOT
			  )
		  {
			  nOriginalInputfields = 1;
				// there is only one entry field, but the arrow button has returned graph range intervals
			  //  as extra fields.
			// Update the value of the (only) input field
			 inputField.value = list[0];
			// Now for the extra fields
			let IDroot = inputField.id;   // no number at the end in these problemtypes
			let NameRoot = IDroot.slice(0, -2); // e.g., "linearEquation" from "linearEquationID"
			let entryFieldsContainer = document.getElementById("EntryFields");

			// First, remove any fields that are no longer needed
			const existingFields = entryFieldsContainer.querySelectorAll(".function-entry");
			existingFields.forEach((field, index) => {
				if (index >= nOriginalInputFields) {  // don't remove the original fields
					entryFieldsContainer.removeChild(field); // Remove extra fields
					// console.log("removing", field);
					// console.log("with index ",index);
				}		
			});
			// Now ensure we have the correct number of fields
			addNewFields(nproblems,IDroot,NameRoot,list);
		  }
	
	else {	// multiple inputs, e.g., for solving linear equations, or minmax, or related rates 
				// or even a single graph with range intervals specified 
				//  Then there should be an element with ID 'EntryFields'
		 let IDroot = stripDigitAtEnd(inputField.id); // e.g., "linearEquationID" from "linearEquationID3"
		 let NameRoot = IDroot.slice(0, -2); // e.g., "linearEquation" from "linearEquationID"
		 console.log("IDroot = ", IDroot);
		 console.log("NameRoot = ",NameRoot);
		 let entryFieldsContainer = document.getElementById("EntryFields");

		// First, remove any fields that are no longer needed
		const existingFields = entryFieldsContainer.querySelectorAll(".function-entry");
		 // except for the following exceptions, there are two input fields, index 0 and 1, with IDs ending in 1 and 2, in the HTML 
	
		existingFields.forEach((field, index) => {
			if (index >= nOriginalInputFields) {  
				entryFieldsContainer.removeChild(field); // Remove extra fields
				console.log("removing", field);
				console.log("with index     ", index);
				console.log("problemtype = ", problemtype);
			}		
		});
		if (problemtype ==  10 || problemtype == 11){  // ODE2 and ODESYSTEM in mainchoi.h
			// then the parameter interval and initial conditions need to be filled.
			// They are not supplied by the Problem Library.  Since those fields are 
			// not inside the EntryFields div,  they have not been removed above.
			let secondEquationElement = document.getElementById("shadowODE2InputID2");
			let intervalElement = document.getElementById("shadowODE2InputID3");
			let initialConditionsElement = document.getElementById("shadowODE2InputID4");
			secondEquationElement.value = list[1];
			intervalElement.value = "0 <= t <= 2 pi";
			initialConditionsElement.value = "1,1";
		}
		if (problemtype === 206) { // RELATED_RATES
			let entryFieldsContainer2 = document.getElementById("EntryFields2");

			// Remove all .function-entry elements
			// This CANNOT be correctly done by using querySelectorAll to get all the fields to be removed
			// and then removing them, because as you remove them the DOM changes. You have to do it this way.
			// (Thanks to ChatGPT for this correction).
			while (entryFieldsContainer2.firstChild) {
				console.log("removing2", entryFieldsContainer2.firstChild);
				entryFieldsContainer2.removeChild(entryFieldsContainer2.firstChild);
			}
		}
		if(topic == 37 || topic == 38 || topic == 39){
			// fill in the from, to, nintervals, and riemannStyle fields.
			document.getElementById(IDroot).value = list[0];
			let fromField = document.getElementById('shadowfrom');
			let toField = document.getElementById('shadowto');
			let nintervalsField = document.getElementById('shadownintervals');
			fromField.value = list[1];
			toField.value = list[2];
			nintervalsField.value = list[3];
			if(topic == 37){
				// update the riemannStyles data with list[4]
				setRiemannStyleSelector(list[4], 'EntryFields');				
			}
		}
			
		else{
			addNewFields(nproblems,IDroot,NameRoot,list);
		}
	}
	// Update the topic with newTopic
	topic = newTopic;   // topic is a global variable defined in the calling environment
	// Update the hidden field that posts the new topic when the Display button is pressed.
	const topicField = document.getElementById('topicField3');
	if (topicField) {
		topicField.value = newTopic;
		console.log("setting topicField3.value = ", newTopic)
	}
	complexSelector = document.querySelector('.selector'); // Selects the first element with the class "selector"
	if (complexSelector == null) {
	    console.error("Selector element with class 'selector' not found in the document. Not necessarily an error.");
	}
	if (complexSelector && problemtype != 1) {  // 1 is SIMPLIFY.  We don't set the selector on SIMPLIFY as it disappears anyway when Display is clicked. 
		// Check if newTopic exists as an option
		let optionExists = Array.from(complexSelector.options).some(
			(option) => parseInt(option.value) === newTopic
		);
		if(optionExists){
			// Set the value of the selector to the new topic
			complexSelector.value = newTopic;
		}	
		// If newTopic doesn't exist, set the selector to some 
		// existing option.   This has to be case-by-case.
		// in some cases it's duplicated, e.g. in EnterSolveEquation for SOLVE_EQUATION, so this 
		// code is in that case superfluous and the result overwritten. 
		var newValue;
		if (!optionExists) {
			if(problemtype == 10)  // FACTOR
				{
					newValue = 85;  // factor_quadratic
				}
			else if(problemtype == 13)  // SOLVE_EQUATION
				{
					// the existing options are 98 and 131, for real and complex roots.
					if(newTopic == 127 || newTopic == 128) // complex_quadratics, complex_cubics
						newValue = 131;
					else
						newValue = 98;
				}	
			else if(problemtype == 105) // TRIG_IDENTITY
				{  // at present there is no selector in EnterVerifyIdentity.php so this is unused code
					newValue = 111;
				}
			else
				console.log("oops, you forgot a case in ArrowButton.js");
			complexSelector.value = newValue;
			console.log("arrowButton.js line 329, setting complexSelector.value to ", newValue);
		}
	}	
	// Simulate a click on the displayButton (which will have an ID like displayButton7)
	const displayButton = document.querySelector('[id^="displayButton"]');
	if (displayButton) {
		displayButton.click();
	} 	
} 

async function handleArrowClick(inputField, problemtype, topicIn) {
	// Handler code for the arrow button
	let param = problemtype.toString() + "+" + topicIn.toString();
	console.log("problemtype in handleArrowClick is ", problemtype);
	console.log("inputField in handleArrowClick is ", inputField);
	try {
			// Wait for the response from the engine
			let response = await sendMessageToEngine("randomProblem", param);
			console.log("Response from sendMessageToEngine:", response);

			// Ensure response is a string before splitting
			if (typeof response !== "string") {
				console.error("Response is not a string:", response);
				return; // Exit if response is not valid
			}

			// Extract the topic and problemtext;
			// the response has the form topic@problemtext.
			let list = response.split("@");  // Parseable text can't contain '@'	
			let newTopic = parseInt(list[0]);
			let problemText = list[1];
			console.log("Response from randomProblem was", newTopic, problemText);
			presentProblem(newTopic, problemText, inputField, problemtype);
	}
	catch (error) {
			console.error("Error in handleArrowClick:", error);
	}
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists