Sindbad~EG File Manager
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