Sindbad~EG File Manager

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

class Parameter {
    constructor(name, value, increment, index) {
        this.name = name;
        this.value = value;
        this.increment = increment;
        this.index = index;
    }
}

let parameters;   // defined globally so it will be accessible

function nextPowerOf10(t) {
    const value = 2 * t;
    const nextPower = Math.ceil(Math.log10(value));
    return Math.pow(10, nextPower);
}

// adjustParameter puts up a popup with lower left corner at x,y in window coordinates
// That popup allows the user to adjust one parameter's value and increment.
// index is the index of that parameter in the parameters array. 
// You have to load translateEnterPages.js before this file, to enable translations

function allowOnlyDigits(event) {
    event.target.value = event.target.value.replace(/\D/g, ''); // Remove any non-digit characters
}

function allowOnlyNumbers(event) {
    // Allow control keys like Backspace, Delete, Arrow keys
    if (event.ctrlKey || event.metaKey || ["Backspace", "Delete", "ArrowLeft", "ArrowRight"].includes(event.key)) {
        return true;
    }
    
    // Allow only digits and a single decimal point
    if (!/^[0-9.]$/.test(event.key)) {
        event.preventDefault();
        return false;  // Explicitly return false to stop the event
    }

    // Prevent multiple decimal points
    if (event.key === "." && event.target.value.includes(".")) {
        event.preventDefault();
        return false;  // Explicitly return false to stop the event
    }

    return true;  // Allow valid input
}

function blockPlusMinusKeys(event) {
    if (event.key === "+" || event.key === "-" || event.key === '=') {
        event.preventDefault();
        event.stopPropagation(); // Ensure it doesn't reach other listeners
    }
}

function preventParameterChange(event) {
    if (event.key === "+" || event.key === "-") {
        event.preventDefault();
        event.stopPropagation();
    }
}


function adjustParameter(x, y, index, container) {
    // Create the parameters array.  This function isn't called until
	 // the DOM has loaded, and there must be parameters, so jsonParameterData will exist.
	 console.log("in adjustParameter with ", x, y);
    if (!parameters) {
         parameters = jsonParameterData.parameters || [];
			console.log("oops!  Shouldn't happen")
     }
	  // Find the parameter with the matching index field
    const parameter = parameters.find(param => param.index === index);
    // Check if a matching parameter was found
    if (!parameter) {
        console.error(`No parameter found with index: ${index}`);
        return;
    }
	let pvalue = parameter.value;  // old value
	let pincrement = parameter.increment;  // old increment 
	let pname = parameter.name;
	let integerNames = ["n","N","m","M","k","K"];
	let ptype =  integerNames.includes(pname) ? 0 : 1;   // 0 means integer, 1 means real

// Disable the global event listener for + and -
   window.addEventListener("keydown", blockPlusMinusKeys, true);
	// Create a "Done" button to close the dialog
	const doneButton = document.createElement('button');
	doneButton.textContent =  translations[languageName].done;
	doneButton.style.display = 'block';
	doneButton.style.margin = '0 auto';
	doneButton.id = "donebutton";  // needed for translation 
	function handleEnterKey(event) {
	    if (event.key === "Enter") {
	        event.preventDefault(); // Prevents any accidental form submission or new lines
	        doneButton.click(); // Simulates clicking the Done button
	    }
	}
	// Add event listener to detect Enter key press
	window.addEventListener("keydown", handleEnterKey);
	// Add event listener to re-enable key handlers when the popup is closed
	doneButton.addEventListener('click', () => {
		popup.remove();
	   window.removeEventListener("keydown", blockPlusMinusKeys, true); // Re-enable keys after closing
	   window.removeEventListener("keydown", handleEnterKey); // Clean up event listener
	});
	
    // Create a container for the sliders and the Done button
    const popup = document.createElement('div');
	 popup.className = 'popup-container'; // Assign the CSS class
	 
  // Position the popup using the x and y coordinates
    popup.style.position = 'absolute';
    popup.style.left = `${x}px`;
    popup.style.top = `${y}px`;
    popup.style.zIndex = '5000'; // Ensure it appears above other elements
 	 popup.style.display = 'flex';
 	 popup.style.flexDirection = 'column';

	const valueLabel = document.createElement('label');
	valueLabel.innerHTML = `<em>${parameter.name}</em> = `;
	valueLabel.style.marginBottom = '5px';
	valueLabel.style.display = 'block';
	valueLabel.style.width = 'auto';
	valueLabel.style.whiteSpace = 'nowrap'; // ✅ Forces it to stay on one line
	valueLabel.style.paddingLeft = '63px';
	valueLabel.style.boxSizing = 'border-box';  // to count the padding as part of the width
	valueLabel.style.textAlign = 'right'; 
	// Create the input field for the parameter value
	
	const valueInput = document.createElement('input');
	valueInput.type = 'text';
	valueInput.id = 'valueInput';
	if(ptype == 0){
		valueInput.pattern="\d*";
		valueInput.inputmode="numeric";
		valueInput.addEventListener("input", allowOnlyDigits);
		valueInput.value = parameter.value;
		//  <input type="text" id="digitInput" pattern="\d*" inputmode="numeric">
	}
	else{
		//<input type="text" id="numberInput" onkeydown="return allowOnlyNumbers(event)">
		valueInput.addEventListener('keydown', allowOnlyNumbers);
		valueInput.value = parameter.value.toString();  // Preserve all decimals exactly
	}
	valueInput.style.marginBottom = '5px';
	valueInput.style.display = 'block';
	valueInput.style.boxSizing = 'border-box';
	valueInput.style.border = '1px solid #ccc';
	valueInput.style.fontSize = 'inherit'; // Match the surrounding text
	valueInput.style.flex = '1'; // Input field takes up the remaining space
	valueInput.style.minWidth = '50px'; 
	valueInput.style.textAlign = 'left';
	valueInput.style.flexGrow = '1'; // Allows input to expand while label stays fixed


	// Event listener to update parameter when user types a valid number
	valueInput.addEventListener('change', function () {
		if (ptype == 0) {
			parameter.value = valueInput.value;
		}
		else{
	     const parsedValue = parseFloat(valueInput.value.replace(/,/g, '')); // Remove formatting commas
	     if (!isNaN(parsedValue)) {
	         parameter.value = parsedValue;
	         console.log("Parameter updated:", parameter.value);
	     } else {
	         alert("Please enter a valid number.");
	         valueInput.value = parameter.value; // Reset to last valid value
	     }
	  }
	 });
	

    const valueSlider = document.createElement('input');
	 valueSlider.type = 'range';
	// Calculate t as the absolute value of the current parameter value
	 const t = Math.abs(parameter.value);
	 const T = nextPowerOf10(t);

	// Modify the value slider range (modifying the min, max, and value)
	 if(ptype == 0){
		 valueSlider.min = 1;
		 if(T < 200)
			 valueSlider.step = 1;
		 else
			 valueSlider.step = parseInt(T/100);  // so it's at least 1
	}
	else{
		valueSlider.min = -T; // Set minimum to -t (as a string for the input element)
		valueSlider.step = T * 0.02;
	}
	valueSlider.max = T;  // Set maximum to t (as a string for the input element)
	valueSlider.value = parameter.value;
	console.log("setting slider.value to ", parameter.value);
	valueSlider.style.width = '100%';
	valueSlider.style.marginBottom = '10px';
	valueSlider.id = 'parameterSlider';
	valueSlider.addEventListener('input', function () {
		if(ptype == 0){
			parameter.value = valueSlider.value;
			valueInput.value = valueSlider.value;
		}
		else{
			parameter.value = parseFloat(valueSlider.value);
			valueInput.value = parameter.value.toFixed(2); // Update input field
		}
		console.log("Parameter updated from slider:", parameter.value);
	 });

	// Create the label and slider for the increment
	const incrementLabel = document.createElement('label');
	const S = nextPowerOf10(parameter.increment);
	const changes = translations[languageName].changes;
	const by = translations[languageName].by;
	incrementLabel.innerHTML = `+/- ${changes} <em>${parameter.name}</em> ${by} `;
	incrementLabel.style.marginBottom = '5px';
	incrementLabel.style.display = 'block';
	incrementLabel.style.width = '100%';

	const incrementSlider = document.createElement('input');
	incrementSlider.type = 'range';
	incrementSlider.max = S;
	if(ptype == 0){
		incrementSlider.min = 1;
		incrementSlider.step = 1;
	}
	 else{
	 	incrementSlider.min = 0;
		incrementSlider.step = S * 0.01;
	 }
    incrementSlider.value = parameter.increment;
    incrementSlider.style.width = '100%';
    incrementSlider.style.marginBottom = '10px';
	 incrementSlider.id = 'incrementSlider'
	

	// Create an input field for the increment 
	const incrementInput = document.createElement('input');
	incrementInput.id = 'incrementInput';
	incrementInput.type = 'text';
	if(ptype == 0){
		incrementInput.pattern="\d*";
		incrementInput.inputmode="numeric";
		incrementInput.value = parameter.increment;
		//  <input type="text" id="digitInput" pattern="\d*" inputmode="numeric">
		incrementInput.addEventListener("input", allowOnlyDigits);
	}
	else{	 
		incrementInput.addEventListener('keydown', allowOnlyNumbers);
		incrementInput.value = new Intl.NumberFormat('en-US', {
			minimumFractionDigits: 2,
			maximumFractionDigits: 10,
		}).format(parameter.increment);
	}
	incrementInput.style.marginBottom = '5px';
	incrementInput.style.display = 'block';
	incrementInput.style.boxSizing = 'border-box';
	incrementInput.style.border = '1px solid #ccc';
	incrementInput.style.fontSize = 'inherit'; // Match the surrounding text
	incrementInput.style.flex = '1'; // Input field takes up the remaining space
	incrementInput.style.minWidth = '50px'; 
	incrementInput.style.textAlign = 'left';
	incrementInput.style.flexGrow = '1'; // Allows input to expand while label stays fixed
	 
	// Event listener to update increment when user types a valid number
	incrementInput.addEventListener('change', function () {
		if(ptype == 0){
			incrementInput.value = 1;    // without making a fuss 
			console.log("got here");
			if (incrementInput.value === "0") {
					incrementInput.value = "1";  // Clear the input field.  But this only works when the focus changes, not immediately
				} 
			parameter.increment = parseInt(incrementInput.value);
		}
		else{
			const parsedValue = parseFloat(incrementInput.value.replace(/,/g, '')); // Remove formatting commas
			if (isNaN(parsedValue) || parsedValue <= 0) {  
				incrementInput.value = parameter.increment; // Reset to last valid value
			} else {
				parameter.increment = parsedValue;
				console.log("Increment updated:", parameter.increment);
			}
		}
	 });
	 incrementInput.addEventListener('input', function () {
	     if (ptype === 0) {  // Integer parameters (n, m, k)
	         if (this.value === "0") {
	             this.value = "1"; // Force it to 1
	         } else if (/^0\d+/.test(this.value)) {
	             this.value = this.value.replace(/^0+/, '');  // Remove leading zeros
	         }
	     } else {  // Real-valued parameters
	         if (this.value === "0") {
	             this.value = "";  // Prevent standalone zero
	         }
	         // Ensure leading zeros are only removed for whole numbers, not decimals
	         else if (/^0\d+/.test(this.value) && !this.value.startsWith("0.")) {
	             this.value = this.value.replace(/^0+/, '');  
	         }
	     }
	 });

	 // Create a container for the value label and input field
	 const valueContainer = document.createElement('div');
	 valueContainer.style.display = 'flex';
	 valueContainer.style.alignItems = 'center'; // Align text and input
	 valueContainer.style.gap = '5px'; // Small spacing between them
	 valueContainer.style.width = '100%';
	 valueContainer.style.justifyContent = 'center'; // Center the pair
	 valueContainer.style.margin = '0 auto'; // Ensures centering in the parent
	 // Append the label and input to this container
	 valueContainer.appendChild(valueLabel);
	 valueContainer.appendChild(valueInput);
	 
	 // Create a container for the increment label and input field
	 const incrementContainer = document.createElement('div');
	 incrementContainer.style.display = 'flex';
	 incrementContainer.style.alignItems = 'center'; // Align text and input
	 incrementContainer.style.gap = '5px'; // Small spacing between them
	 incrementContainer.style.width = '100%';
	 incrementContainer.style.justifyContent = 'center'; // Center the pair
	 incrementContainer.style.margin = '0 auto'; // Ensures centering in the parent
	 // Append the label and input to this container
	 incrementContainer.appendChild(incrementLabel);
	 incrementContainer.appendChild(incrementInput);	

	 // Append the containers to the popup
	 popup.style.alignItems = 'center'; // Ensures everything inside is centered
	 popup.appendChild(valueContainer);
	 popup.appendChild(valueSlider);
	 popup.appendChild(incrementContainer);
	 popup.appendChild(incrementSlider);
	 popup.appendChild(doneButton);

    // Append the popup 
	container.appendChild(popup);
	AddEventListeners();

	// Update the parameter value when the user adjusts the valueSlider
	valueSlider.addEventListener('input', () => {
		if(ptype == 0)
			parameter.value = valueSlider.value;
		else{
			parameter.value = parseFloat(valueSlider.value);
			valueInput.value = parameter.value.toFixed(2); // Update the input field's value
		}
	 });

    // Update the parameter increment when the user adjusts the incrementSlider
    incrementSlider.addEventListener('input', () => {
        parameter.increment = parseFloat(incrementSlider.value);
		  console.log("parameter is ", parameter);
        incrementInput.value = parameter.increment; 
        console.log(`Parameter ${index} increment updated to: ${parameter.increment}`);
    });
    // Remove the popup when the "Done" button is clicked
	 // Remove the popup and update parameters when the "Done" button is clicked
	 doneButton.addEventListener('click', () => {
		 popup.remove();
		// Check if parameters[index] has had its value and/or increment adjusted  
		console.log("New increment: ", parameters[index].increment);
		if(parameters[index].value == pvalue && parameters[index].increment != pincrement) 
			{ //  then the screen doesn't change, so before there were parameter sliders, I could just send an Ajax message.
			  //  But now, if the increment changes, the Engine will need to send back the new increment and a bunch of 
				// new invisible graphs for the slider to show.  The old code is in record4.txt dated 3.10.25
				updateParameters();
			} 
		if(parameters[index].value != pvalue) 
			{ 	// then the screen must change, so we have to submit a hidden form with the  
				// new values of the parameters and their increments 
				updateParameters();
			}
	   // and if nothing has changed, do nothing.
	 });
}

function updateParameters()
// this is called when adjustParameter terminates and a parameter value has changed.
// Submit a hidden form to PHP_SELF, which (when processed) will send an "update Parameters" message to the Engine.
{
	// Assuming `parameters` is a global array of `Parameter` objects
	let parametersString = parameters.map(param => `${param.name}#${param.value}#${param.increment}#${param.index}`).join(';');
	// example, "a#1.23#0.1#0;b#4.56#0.2#1"
	console.log("in updateParameters with ", parametersString);
	// Create a hidden form element
	const form = document.createElement('form');
	form.method = 'POST';
	form.action = window.location.href; // PHP_SELF equivalent

	// Create a hidden input field for the JSON data
	const hiddenField = document.createElement('input');
	hiddenField.type = 'hidden';
	hiddenField.id = 'parameters';
	hiddenField.name = 'parametersField';
	hiddenField.value = parametersString;


	// Append the hidden field to the form
	form.appendChild(hiddenField);

	// Append the form to the body (it must be in the DOM to be submitted)
	document.body.appendChild(form);

	// Submit the form
	form.submit();
}

function AddEventListeners() {
	const slider = document.getElementById('parameterSlider');
	const incrementSlider = document.getElementById('incrementSlider');
	const incrementInputField = document.getElementById('incrementInput');
	const inputField = document.getElementById('valueInput');  
	const doneButton = document.getElementById('donebutton'); // New Done button
	console.log("slider is ", slider);

	 
	// Function to update increment from slider
	function updateIncrementFromSlider() {
		incrementValue = parseFloat(incrementSlider.value);
		incrementInputField.value = ''; // Clear input field when using the slider
		// console.log("Increment updated from slider:", parameterValue);
	}

	// Function to update parameter from input field
	function updateParameterFromInput() {
		const inputValue = inputField.value.trim();
		const parsedValue = parseFloat(inputValue);

		if (!isNaN(parsedValue)) {
			parameterValue = parsedValue;
			slider.value = parameterValue; // Sync slider
			// console.log("Parameter updated from input:", parameterValue);
		} else {
			alert("Please enter a valid number."); // can never happen as only numbers can be input 
		}
    }
	 
    // Function to update increment from input field
    function updateIncrementFromInput() {
        const inputValue = incrementInputField.value.trim();
        const parsedValue = parseFloat(inputValue);

        if (!isNaN(parsedValue)) {
            incrementValue = parsedValue;
            incrementSlider.value = incrementValue; // Sync slider
           // console.log("Increment updated from input:", incrementValue);
        } else {
            alert("Please enter a valid number.");
        }
    }

    // Event listener for the increment slider
    incrementSlider.addEventListener('input', updateIncrementFromSlider);
	 

    // Event listeners for the "Done" button
	 doneButton.addEventListener('click', function () {
	     updateParameterFromInput();
	     updateIncrementFromInput();
	 }); 
 }


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