jCrop resize with correct coords when changing image
<p>Hoping this doesn't get flagged as a duplicate because none of the other q/as on SO have helped me fix this, I think I need a more specific line of help.</p>

<p>I have a profile page on my site that allows the user to change their profile picture without page reloads (via AJAX / jQuery).</p>

<p>This all works fine. The user opens the "Change Profile Picture" modal, selects a file to upload and presses "Crop this image". When this button is pressed, it uploads a file to the website, using the typical way of sending the file and formData (which I append the file data to).</p>

<p>It gets sent backend with the following jQuery:</p>

<pre><code>// Upload the image for cropping (Crop this Image!)
$("#image-upload").click(function(){

    // File data
    var fileData = $("#image-select").prop("files")[0];

    // Set up a form
    var formData = new FormData();

    // Append the file to the new form for submission
    formData.append("file", fileData);

    // Send the file to be uploaded
    $.ajax({

        // Set the params
        cache: false,
        contentType: false,
        processData: false,

        // Page &amp; file information
        url: "index.php?action=uploadimage",
        dataType: "text",
        type: "POST",

        // The data to send
        data: formData,

        // On success...
        success: function(data){

            // If no image was returned
            // "not-image" is returned from the PHP script if we return it in case of an error
            if(data == "not-image"){
                alert("That's not an image, please upload an image file.");
                return false;
            }

            // Else, load the image on to the page so we don't need to reload
            $(profileImage).attr("src", data);

            // If the API is already set, then we should apply a new image
            if(jCropAPI){
                jCropAPI.setImage(data + "?" + new Date().getTime());
            }

            // Initialise jCrop
            setJCrop();

            //$("#image-profile").show();
            $("#send-coords").show();
        }
    })
});
</code></pre>

<p>setJcrop does the following</p>

<pre><code>function setJCrop(){

    // Get width / height of the image
    var width = profileImage.width();
    var height = profileImage.height();

    // Var containing the source image
    var imgSource = profileImage.attr("src");

    // New image object to work on
    var image = new Image();
    image.src = imgSource;

    // The SOURCE (ORIGINAL) width / height
    var origWidth = image.width;
    var origHeight = image.height;

    // Set up the option to jCrop it
    $(profileImage).Jcrop({
        onSelect: setCoords,
        onChange: setCoords,
        setSelect: [0, 0, 51, 51],
        aspectRatio: 1,             // This locks it to a square image, so it fits the site better
        boxWidth: width,
        boxHeight: height,          // Fixes the size permanently so that we can load new images
    }, function(){jCropAPI = this});

    setOthers(width, height, origWidth, origHeight);
}
</code></pre>

<p>And once backend, it does the following:</p>

<pre><code>public function uploadImage($file){

    // See if there is already an error
    if(0 &lt; $file["file"]["error"]){
        return $file["file"]["error"] . " (error)";
    }else{

        // Set up the image
        $image = $file["file"];
        $imageSizes = getimagesize($image["tmp_name"]);

        // If there are no image sizes, return the not-image error
        if(!$imageSizes){
            return "not-image";
        }

        // SIZE LIMIT HERE SOON (TBI)

        // Set a name for the image
        $username = $_SESSION["user"]-&gt;getUsername();
        $fileName = "images/profile/$username-profile-original.jpg";

        // Move the image which is guaranteed a unique name (unless it is due to overwrite), to the profile pictures folder
        move_uploaded_file($image["tmp_name"], $fileName);

        // Return the new filename
        return $fileName;
    }
}
</code></pre>

<p>Then, the user selects their area on the image with the selector and pressed "Change Profile Picture" which does the following</p>

<pre><code>// Send the Coords and upload the new image
$("#send-coords").click(function(){

    $.ajax({
        type: "POST",
        url: "index.php?action=uploadprofilepicture",
        data: {
            coordString: $("#coords").text() + $("#coords2").text(),
            imgSrc: $("#image-profile").attr("src")
        },
        success: function(data){
            if(data == "no-word"){
                alert("Can not work with this image type, please try with another image");
            }else{

                // Append a date to make sure it reloads the image without using a cached version
                var dateNow = new Date();
                var newImageLink = data + "?" + dateNow.getTime();
                $("#profile-picture").attr("src", newImageLink);

                // Hide the modal
                $("#profile-picture-modal").modal("hide");
            }
        }
    });
})
</code></pre>

<p>The backend is:</p>

<pre><code>public function uploadProfilePicture($coordString, $imgSrc){

    // Target dimensions
    $tarWidth = $tarHeight = 150;

    // Split the coords in to an array (sent by a string that was created by JS)
    $coordsArray = explode(",", $coordString);

    //Set them all from the array
    $x =            $coordsArray[0];
    $y =            $coordsArray[1];
    $width =        $coordsArray[2];
    $height =       $coordsArray[3];
    $newWidth =     $coordsArray[4];
    $newHeight =    $coordsArray[5];
    $origWidth =    $coordsArray[6];
    $origHeight =   $coordsArray[7];

    // Validate the image and decide which image type to create the original resource from
    $imgDetails = getimagesize($imgSrc);
    $imgMime = $imgDetails["mime"];

    switch($imgMime){
        case "image/jpeg":
            $originalImage = imagecreatefromjpeg($imgSrc);
            break;
        case "image/png":
            $originalImage = imagecreatefrompng($imgSrc);
            break;
        default:
            return "no-work";
    }

    // Target image resource
    $imgTarget = imagecreatetruecolor($tarWidth, $tarHeight);
    $img = imagecreatetruecolor($newWidth, $newHeight);

    // Resize the original image to work with our coords
    imagecopyresampled($img, $originalImage, 0, 0, 0, 0,
        $newWidth, $newHeight, $origWidth, $origHeight);

    // Now copy the CROPPED image in to the TARGET resource
    imagecopyresampled(
        $imgTarget,     // Target resource
        $img,           // Target image
        0, 0,           // X / Y Coords of the target image; this will always be 0, 0 as we do not want any black nothingness
        $x, $y,         // X / Y Coords (top left) of the target area
        $tarWidth,
        $tarHeight,       // width / height of the target
        $width,
        $height         // Width / height of the source image crop
    );

    $username = $_SESSION["user"]-&gt;getUsername();
    $newPath = "images/profile/$username-profile-cropped.jpg";

    // Create that shit!
    imagejpeg($imgTarget, $newPath);

    // Return the path
    return $newPath;
}
</code></pre>

<p>So basically this the returns the path of the new file, which gets changed to the user's profile picture (same name every time) and uploaded live with a time appended after <code>?</code> to refresh the image properly (no cache).</p>

<p>This all works fine, however if the user selects another image to upload, after already uploading one, the coords get all messed up (e.g. they go from 50 to 250) and they end up cropping a totally different part of the image, leaving most of it black nothing-ness.</p>

<p>Really sorry for the ridiculous amount of code that is in this question but I'd appreciate any help from people who might have worked around this before.</p>

<p>Some of the code may seem out of place but that's just me trying to debug it.</p>

<p>Thanks, and again, sorry for the size of this question.</p>

<p>-Edit-</p>

<p>My <code>setCoords()</code> and <code>setOthers()</code> functions look like so:</p>

<pre><code>//Set the coords with this method, that is called every time the user makes / changes a selection on the crop panel
function setCoords(c){
    $("#coords").text(c.x + "," + c.y + "," + c.w + "," + c.h + ",");
}

//This one adds the other parts to the second div; they will be concatenated in to the POST string
function setOthers(width, height, origWidth, origHeight){
    $("#coords2").text(width + "," + height + "," + origWidth + "," + origHeight);
}
</code></pre>