AJAX responseText issue? (Dealing with creating an AJAX/PHP Upload an image code

Hello!
I have been working on making a form that uploads an image through ajax with php doing the upload. For some background I am having an issue where the xhr.readyState and .Status seem to run and upload.php seems to run (in safari it goes and runs upload.php it seems to me… I have not used the ajax/javascript debugger too much) but it will run my else statement (i believe becuase it goes to status 2 and then 4) and then run xhr.responseText. Unfortunately it does not seem to run the upload.php right (which may be a php problem). I went ahead and replaced the php with a print statement which when running alert(xhr.response.Text) comes up with a blank alert statement and I am at a loss where the issue is. Below is my form code, ajax code and the php code i’m trying to get a response from.

Does anyone see the issue I am having? I am new to AJAX and have not worked with PHP in some time. I am working on upping my ajax/javascript skills but from my view it does not seem to be getting the ajax correctly.

HTML CODE


<form action="scripts/upload.php" method="post" enctype="multipart/form-data">
	    <label>Select a File to Upload</label> <input id="upload_file" type="file" name="upload_file" />
	    <input type="button" onclick="loadFile()" value="Upload"  />
	</form>

JAVASCRIPT/AJAX CODE


//LOAD IMAGES THROUGH JAVASCRIPT WINDOW.ONLOAD = INIT();

$(document).ready(function() {
  // Handler for .ready() called.

  $('.heading').click(function() {
	  if($(this).find('li').is(':visible')) {	
	  	$(this).find('li').slideUp('slow');	
  	}else {
  	  $('.heading').find('li').slideUp('slow');
	  $(this).find('li').slideDown('slow');	
  	}
  })
});

var xhr = createRequest();

function loadFile() {
//retrieve the FileList object from the referenced element ID
	var myFileList = document.getElementById('upload_file').files;
	
	//grab the first file object from the filelist
	var myFile = myFileList[0];
	
	//set some variables containing attributes of file
	var myFileName = myFile.name;
	var myFileSize = myFile.size;
	var myFileType = myFile.type;
	
	//alert the information gathered and if it is right
	alert("FileName: " + myFileName + "- FileSize: " + myFileSize + " - FileType: " + myFileType);
	
	//check above by alert if file size is below a certain MB and of image type JPEG, PNG, GIF
	
	//Let's upload the complete file object
	uploadFile(myFile);
}

function uploadFile(myFileObject) {
	// Open Our formData Object
	var formData = new FormData();

	// Append our file to the formData object
	// Notice the first argument "file" and keep it in mind
	formData.append('my_uploaded_file', myFileObject);

	// Create our XMLHttpRequest Object
	xhr.onreadystatechange = addImage;
	
	// Open our connection using the POST method
	xhr.open("POST", 'upload.php');
	//Send the proper header information along with the request
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	
	// Send the file
	xhr.send(formData);
}

function addImage() {
	if (xhr.readyState === 4 && xhr.status === 200) {
        alert("WIN");
        alert(xhr.responseText);
    } else {
	    alert("ERROR ERROR: " + xhr.status);
    }
}

PHP


<?php
	print("HELLO");
?>

<?php
/*
define("UPLOAD_DIR", "../images");

if (!empty($_FILES["myFile"])) {
    $myFile = $_FILES["myFile"];

    if ($myFile["error"] !== UPLOAD_ERR_OK) {
        echo "<p>An error occurred.</p>";
        exit;
    }

    // ensure a safe filename
    $name = preg_replace("/[^A-Z0-9._-]/i", "_", $myFile["name"]);

    // don't overwrite an existing file
    $i = 0;
    $parts = pathinfo($name);
    while (file_exists(UPLOAD_DIR . $name)) {
        $i++;
        $name = $parts["filename"] . "-" . $i . "." . $parts["extension"];
    }

    // preserve file from temporary directory
    $success = move_uploaded_file($myFile["tmp_name"],
        UPLOAD_DIR . $name);
    if (!$success) {
        echo "<p>Unable to save file.</p>";
        exit;
    }

    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);
}
*/
?>

I pasted all my PHP code although right now i’m just working with the print PHP code to get a response, I am sure there are many things (besides security checking) with my upload php code. Any help with where the issue is to get this image upload situation working would be great as then I can work on my DRAG & DROP API

Hi there,

the problem is this line:

var xhr = createRequest();

createRequest() is not defined, so by the time you get here:

xhr.onreadystatechange = addImage;

the variable xhr is undefined and your script falls over and dies.

Also, please note that you will never see any output from the PHP script, as it is being called via AJAX.
This is not the same as navigating to the page in your browser.

my apologies. I forgot to past a function from another javascript file.


function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (tryMS) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (otherMS) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = null;
      }
    }
  }	
  return request;
}

This should define the javascript variable and add the correct Request depending upon browser (although event is not added to it), if i understand correctly?
Is there any way to see output from your script? I understand that it doesn’t navigate to another page but I thought it still could return the succes/failures of the script to responseText? I may have to go back to responseText and re-read it.

With this new information is my ajax working correctly (besides a few cross browser issues I believe)?

Hi,

Once I added the missing function, your script worked for me.

You might however want to change this:

if (xhr.readyState === 4 && xhr.status === 200) {
  alert("WIN");
  alert(xhr.responseText);
} else {
  alert("ERROR ERROR: " + xhr.status);
}

to this:

if (xhr.readyState === 4 && xhr.status === 200) {
  console.log(xhr.responseText);
}

You can then see the output in the console. You would find this in Chrome (for example) by pressing F12 and selecting the “Console”.

I spent quite a while trying to get your PHP script to work, but kept getting quite bogged down, so I adapted a script from StackOverflow and ported the whole thing to jQuery:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>jQuery File Upload</title>
    <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
  </head>

  <body>
    <form enctype="multipart/form-data">
      <input name="file" type="file" />
      <input type="button" value="Upload" />
    </form>
    <progress></progress>
  
    <script>
      $(':file').change(function(){
        var file = this.files[0];
        name = file.name;
        size = file.size;
        type = file.type;
        //your validation
      });
      
      $(':button').click(function(){
        var formData = new FormData($('form')[0]);
        $.ajax({
          url: 'upload.php',  //server script to process data
          type: 'POST',
          xhr: function() {  // custom xhr
            myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){ // check if upload property exists
              myXhr.upload.addEventListener('progress',progressHandlingFunction, false); // for handling the progress of the upload
            }
            return myXhr;
          },
          success: function(response){
            console.log(response);
          },
          data: formData,
          //Options to tell JQuery not to process data or worry about content-type
          cache: false,
          contentType: false,
          processData: false
        });
      })		
      
      function progressHandlingFunction(e){
        if(e.lengthComputable){
          $('progress').attr({value:e.loaded,max:e.total});
        }
      }
    </script>
  </body>
</html>
<?php 
move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/" . $_FILES["file"]["name"]);
echo "Stored in: " . "uploads/" . $_FILES["file"]["name"];
?> 

The script lets you select a file, then upon submission it will upload it to an “uploads” folder, which it expects to find in the same location as itself.
Upon successful completion, it will log the location of the uploaded file to the console.

I would encourage you to check out the StackOverflow article, as the full script includes more functionality, such as error handlers, which I have left out here for the sake of brevity.

Hope this helps you.

Hello thanks for the reference! I will have to read it over in a bit later tonight when I sit down to finish this up. Yea, I will be changing my PHP around… I actually think i found a major problem about the directory i am choosing currently, haha.

I am wondering why you chose to do error handling on the jQuery side rather than the server side? As well, why you wrote it in jQuery vs. pure Javascript? I am just curious about that… obviously jQuery is more cross browser compatible and easier to use. I have some above in this example only because I don’t know how to make it pure javascript and everything I turn up is jQuery. I need to re-read about traversing the JS DOM.

I will probably adapt my PHP to your script if I can’t figure it out but I do like that PHP is doing most of the work, incase javascript is disabled, with a little index.html changes it can still work without ajax/js. and I did realize my mistake. some previous Form data had the wrong action=“” area. This made my upload.php not run correctly. I must have mixed up my local old copy vs. new copy. I now have responseText working correctly although I haven’t worked on my PHP script yet in terms of progress/error correction.

I will have to keep looking at it to make sure I get it done the right way, haha. Really in the end I want to get this part done as fast as possible, as I want the addImage function to go through and add all images to a list and then from there add Dragon and Drop API into a 500x500 div and even go further to allow drag deletes somehow.

Not “rather than”, but “as well as”.
It makes your app more responsive for your users.

Well, I saw from your first post that you were using jQuery anyway and as jQuery irons out so many browser incompatibility problems for you, it seemed to be the logical choice.
The only reason I can see for not using jQuery is the added dependency, yet considering that the current jQuery.min.js weighs in at 92kb, that didn’t seem like a terrible trade off in this situation.

This only makes a difference if JavaScript is disabled.

You might want to look at this for the drag and drop functionality: http://jqueryui.com/draggable/

Good luck!

Ah I got ya. I plan on doing it I just wanted to get the functionality to work first. The reason I am doing this in pure JS instead of jQuery is to actually better understand the language jQuery is made off of. To learn to do dom manipulation and see the browser inconsistencies and such. I’d prefer to use jQuery too, haha.

I actually have now determined my code DOES work (php and js etc.) … as long as it is NOT going through AJAX but a regular submit. I will have to check it out to determine why yet.

Yeah, that’s why I turned to jQuery, I couldn’t get your code to work either.
The PHP part was no problem, but I couldn’t manage to reference anything in $_FILES once the upload had taken place when doing it in native JS.

I actually determined that I was referencing the wrong variable in js. When doing it with HTML and php my_uploaded_file is referenced for the file information. The form variable was wrong referencing upload_file. That was the issue. I also added on window load prevent form submission if JavaScript is enabled. This makes it load with and without JavaScript.

Now I can finally go ahead and put the files in on the callback function and add a delete button etc haha

Yeah I changed that in my script, but still couldn’t get it to work.
Would you mind posting your working code?

The PHP and Form code are here below:
Everything works although there are a few drag and drop issues that I later implemented that I have to work thorugh. As far as Upload, Delete, progress bar etc. all works in terms of ajax/php.

FORM:::

<form action="scripts/upload.php" method="post" enctype="multipart/form-data">
		    <label>Select a File to Upload</label>
		    <input id="upload_file" type="file" name="upload_file" />
		    <input type="submit" value="Upload" id="inputSubmit" />
		    <input type="button" value="Hover Image Over To Delete" id="DeleteDrag" />
		</form>

PHP:::

<?php
/*
$baseFolder =  dirname(dirname(__FILE__));
$imageFolder = $baseFolder . "/images/";
/*$imagesFolder = $baseFolder . "/images/"*/
/*define("UPLOAD_DIR", "../images");*/
/*echo $baseFolder;
echo "</br>";
echo $imageFolder;*/

$baseFolder =  dirname(dirname(__FILE__));
$imageFolder = $baseFolder . "/images/";
define("UPLOAD_DIR", $imageFolder);

if (!empty($_FILES["my_uploaded_file"])) {
    $myFile = $_FILES["my_uploaded_file"];

    if ($myFile["error"] !== UPLOAD_ERR_OK) {
        echo "<p>An error occurred.</p>";
        exit;
    };


    // ensure a safe filename
    $name = preg_replace("/[^A-Z0-9._-]/i", "_", $myFile["name"]);


    // don't overwrite an existing file
    $i = 0;
    $parts = pathinfo($name);
    while (file_exists(UPLOAD_DIR . $name)) {
        $i++;
        $name = $parts["filename"] . "-" . $i . "." . $parts["extension"];
    }

    // preserve file from temporary directory
    $success = move_uploaded_file($myFile["tmp_name"],
        UPLOAD_DIR . $name);


    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);

    $dirhandler = opendir(UPLOAD_DIR);
    //create handler to directory

    //read all the files from directory
    $nofiles=0;
    while ($file = readdir($dirhandler)) {
	    //if $file isn't this directory or its parent
	    //echo "FILE SYSTEM: " . $file . "<br/>";
	    //add to the $files array
	    if ($file != '.' && $file != '..') {
		    $nofiles++;
		    $files[$nofiles]=$file;
	    } //end of if loop
    } //end of while loop
    //json_encode($files);
    if (!$success) {
    	$nofiles++;
    	$files[$nofiles] = "Unable to upload file";
        exit;
    } else {
    	$nofiles++;
    	$files[$nofiles] = "File Upload Successfully";
    }
    echo json_encode($files);
} else {
	$dirhandler = opendir(UPLOAD_DIR);
    //create handler to directory

    //read all the files from directory
    $nofiles=0;
    while ($file = readdir($dirhandler)) {
	    //if $file isn't this directory or its parent
	    //echo "FILE SYSTEM: " . $file . "<br/>";
	    //add to the $files array
	    if ($file != '.' && $file != '..') {
		    $nofiles++;
		    $files[$nofiles]=$file;
	    } //end of if loop
    } //end of while loop
    //json_encode($files);

    echo json_encode($files);
}

?>

Hey,

Thanks for that, but what I was really after was the JavaScript code.
Would you mind posting that, too?

Ah,
sorry about that javascript code is below:

window.onload = init;

function init() {
	xhr = createRequest();
	xhr2 = createRequest();
	submit = document.getElementById("inputSubmit");
	submit.onclick = inputSubmit;
	
	//submit onclick for Next Page Button
	/*np = document.getElementById("np");
	np.onclick = NextPage;
	
	pp = document.getElementById("pp");
	pp.onclick = PreviousPage;*/
	//first ajax call to load images at beginning
	xhr2.onreadystatechange = getImage;
	xhr2.open("GET", 'http://kdmalik.com/DEVDERBY/scripts/upload.php',true);
	xhr2.send(null);
//Start of DRAG AND DROP INFORMATION
//VARIABLES SHOULD BE CHANGED AS VERY SIMILAR VARIABLES PREVIOUSLY, COMBINE THEM


}

function getImage() {
	if (xhr2.readyState === 4 && xhr2.status === 200) {
        var imageNames = JSON.parse(xhr2.responseText);
        var objSize = getObjectCount(imageNames);
         createImageHtml(imageNames);
         var imagesContainer = document.getElementById("images");

/*var allImages = document.querySelectorAll('#images img');
console.log(allImages);
console.log(allImages.length);*/

//Drag and drop listener to each
deleteBtn = document.getElementById("DeleteDrag");
allImages = imagesContainer.getElementsByTagName("img");
for (i=0; i < allImages.length; i++) {
	  addEventHandler(allImages[i], 'dragstart', handleDragStart);
	addEventHandler(allImages[i], 'dragenter', handleDragEnter);
	  addEventHandler(allImages[i], 'dragover', handleDragOver);
	  addEventHandler(allImages[i], 'dragleave', handleDragLeave);
addEventHandler(allImages[i],'drop',handleDrop);
addEventHandler(allImages[i],'dragend',handleDragEnd);
  }
  //EVENT LISTENER FOR DIV
  //GRAB MAIN DIV
  mainPicture = document.getElementById("mainPictureArea")
  addEventHandler(mainPicture, 'dragstart', handleDragStart);
	addEventHandler(mainPicture, 'dragenter', handleDragEnter);
	  addEventHandler(mainPicture, 'dragover', handleDragOver);
	  addEventHandler(mainPicture, 'dragleave', handleDragLeave);
addEventHandler(mainPicture,'drop',handleDrop);
addEventHandler(mainPicture,'dragend',handleDragEnd);

//DELETE BUTTON HOVER
deleteBtn = document.getElementById("DeleteDrag");
 addEventHandler(deleteBtn, 'dragstart', handleDragStart);
	addEventHandler(deleteBtn, 'dragenter', handleDragEnter);
	  addEventHandler(deleteBtn, 'dragover', handleDragOver);
	  addEventHandler(deleteBtn, 'dragleave', handleDragLeave);
addEventHandler(deleteBtn,'drop',handleDrop);
addEventHandler(deleteBtn,'dragend',handleDragEnd);

    } else if (xhr2.status === 404 || xhr2.status === 500) {
	    alert("Your Upload Request failed.");
    }
}

function handleDragEnd(e) {
  // this/e.target is the source node.

   [].forEach.call(allImages, function (imgs) {
    imgs.classList.remove('over');
    imgs.style.opacity = '1.0';
  });

}

var dragSrcEl = null;

function handleDrop(e) {
  // this / e.target is current target element.
  if (e.stopPropagation) {
    e.stopPropagation(); // stops the browser from redirecting.
  }
  if (e.preventDefault) {
    e.preventDefault(); // stops the browser from redirecting.
  }

  if (dragSrcEl.src != this.src) {
  	if(this == mainPicture) {
	  	var imgCopy = e.dataTransfer.getData('text/html');
	  	var mainImage = document.createElement("img");
	  	mainImage.setAttribute('src', imgCopy);
	  	mainImage.setAttribute('height', '300');
	  	mainImage.setAttribute('width', '300');
	  	mainPicture.innerHTML = "";
	  	mainPicture.appendChild(mainImage);
	  	this.classList.remove('over');
	  	return;
  	}
  	
  	if (this == deleteBtn) {
  		var imgs = 	document.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
    if ("http://kdmalik.com/DEVDERBY/" +imgs[i].getAttribute("src") == dragSrcEl.src) {
        imgs[i].parentNode.removeChild(imgs[i]);
    }
}
		//AJAX CALL TO UPLOAD
		var mainPictureImage = mainPicture.getElementsByTagName("img");
		for (var i=0;i<mainPictureImage.length;i++) {
			if(mainPictureImage[i].getAttribute("src") == dragSrcEl.src)
			mainPicture.removeChild(mainPictureImage[i]);
			mainPicture.innerHTML = "<p>Drag here to view picture!</p>";
		}
		//CALL AJAX FUNCTION TO DELETE!
		xhr3 = createRequest();
		xhr3.onreadystatechange = deleteImage;
	xhr3.open("POST", 'http://kdmalik.com/DEVDERBY/scripts/delete.php',true);
	xhr3.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	xhr3.send(dragSrcEl.src);
	//dragSrcEl.src
  		this.classList.remove('over');
	  	return;
  	}
	  //set thes oruce colum's html to the html of the colum we dropped
	  var imageDrop = this.src;
	  dragSrcEl.src = imageDrop;
	  	  this.src = e.dataTransfer.getData('text/html');
  }
  return false;
}
function deleteImage() {
	if (xhr3.readyState === 4 && xhr3.status === 200) {
		var deletePhrase = document.getElementById("deleteSuccess");
		deletePhrase.innerHTML = "Your File Has Been Deleted";
	} else if (xhr3.status === 404 || xhr3.status === 500) {
	    alert("Your delete Request failed.");
    }

    }
function handleDragStart(e) {
	//alert("test");
	this.style.opacity = '0.4';
	dragSrcEl = this;
	//alert(dragSrcEl);
	e.dataTransfer.effectAllowed='all';
	e.dataTransfer.setData("text/html",dragSrcEl.src);
}

function handleDragOver(e) {
	if (e.preventDefault) {
		e.preventDefault(); //necessary, allows us to drop
	}
		e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object.
		return false;
}

function handleDragEnter(e) {
	//this / e.target is the current hover target
	this.classList.add('over'); // this / e.target is the current hover target.
}

function handleDragLeave(e) {
	//alert(this.classList);
	this.classList.remove('over');
	// this /e.target is previous target element.
	//alert(this.classList);
}

function inputSubmit(evt) {
	evt.preventDefault();
	loadFile();	
}

/*$(document).ready(function() {
  // Handler for .ready() called.

  $('.heading').click(function() {
	  if($(this).find('li').is(':visible')) {	
	  	$(this).find('li').slideUp('slow');	
  	}else {
  	  $('.heading').find('li').slideUp('slow');
	  $(this).find('li').slideDown('slow');	
  	}
  })
});*/


function loadFile() {
//retrieve the FileList object from the referenced element ID
	var myFileList = document.getElementById('upload_file').files;
	
	//grab the first file object from the filelist
	var myFile = myFileList[0];
	
	//set some variables containing attributes of file
	var myFileName = myFile.name;
	var myFileSize = myFile.size;
	var myFileType = myFile.type;
	
	//alert the information gathered and if it is right
	//alert("FileName: " + myFileName + "- FileSize: " + myFileSize + " - FileType: " + myFileType);
	
	//check above by alert if file size is below a certain MB and of image type JPEG, PNG, GIF
	
	//Let's upload the complete file object
	uploadFile(myFile);
}

function uploadFile(myFileObject) {
	// Open Our formData Object
	var formData = new FormData();
	
	// Append our file to the formData object
	// Notice the first argument "file" and keep it in mind
	formData.append('my_uploaded_file', myFileObject);

	// Create our XMLHttpRequest Object
	xhr.onreadystatechange = addImage;
	
	// Open our connection using the POST method
	xhr.open("POST", 'http://kdmalik.com/DEVDERBY/scripts/upload.php',true);
	//Send the proper header information along with the request

	
	// Send the file
	xhr.send(formData);
}


function getObjectCount(obj) {
	for(var i in obj) {
		i++;
	}
	
	return i;
	
}

function createImageHtml(imageNames) {
	var imageContainer = document.getElementById("images");
	imageContainer.innerHTML = '';
	var objSize = getObjectCount(imageNames);
	
	for(var i=1;i < objSize; i++) {
	
		if(imageNames[i] == "File Upload Successfully") {return;}
		
		var cImg = document.createElement("img");
cImg.setAttribute('src',"images/" + imageNames[i]);
		cImg.setAttribute('height', '150');
		cImg.setAttribute('width','150' );
		var parentDiv = document.getElementById("images");
		parentDiv.appendChild(cImg);	
		}
	
}
function addImage() {

var progressBar = document.getElementById("pbar");
var progressText = "";
progressStatus = document.getElementById("status");

	if (xhr.readyState === 4 && xhr.status === 200) {
		progressBar.value="100";
		progressText = "Upload Complete!";
		progressStatus.innerHTML = progressText;
        var imageNames = JSON.parse(xhr.responseText);
         createImageHtml(imageNames);
    } else if (xhr.readyState === 0 || xhr.readyState === 1) {
    	progressBar.value = "25";
    	progressText = "Starting Upload..";
    	progressStatus.innerHTML = progressText;
    } else if (xhr.readyState === 2) {
    	progressBar.value = "50";
    	progressText = "Almost Done...";
    	progressStatus.innerHTML = progressText;
    } else if (xhr.readyState === 3) {
    	progressBar.value = "75";
    	progressText = "So Close!";
    	progressStatus.innerHTML = progressText;
    } else if (xhr.status === 404 || xhr.status === 500) {
	    alert("Your Upload Request failed.");
    }

}

//DRAG SECTION

I have two utility objects I use as well below for event handling and ajax calls

function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (tryMS) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (otherMS) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = null;
      }
    }
  }	
  return request;
}

function addEventHandler(obj, eventName, handler) {
  if (document.attachEvent) {
    obj.attachEvent("on" + eventName, handler);
  } else if (document.addEventListener) {
    obj.addEventListener(eventName, handler, false);
  }
}

Thanks for that.
I’ll have a look at what you posted now and see if I can work out where I was going wrong with your original example.
I’m glad to hear that you got everything working and well done for not opting for the jQuery solution.
That is a great way to learn! :slight_smile:

Thank you. I figured it would be best to learn by the true language rather than jQuery as that deals with a lot of the cross compatibility issues and DOM manipulation that I was trying to hammer down. Looking at the language really (for me) helps me understand what i’m working through. I must admit though, I need to look at my code more carefully as a few of my mistakes made me sigh out loud haha.

If your interested to see what I did check out kdmalik.com/DEVDERBY. Still some kinks to work out in regards to compatibility but everything else should be “ok”. Definitely the code I am sure could use some clean up.