We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Javascipt (jcrop) wont work on uploaded file preview in volt file, same code/files works perfectly in html and blade.

Hello!

I'm using Jcrop to select cordinates to crop a image. The user selects a image, it is shown and then the user selects on the preview where they want to crop. This workes perfectly in a simple .html and on a previous laravel project (blade). Only difference is how I import the js and css, but still the exact same code and files.

However the moment I use .volt then Jcrop refuses to work on the preview. The preview is shown, but I can't drag across it. Note that Jcrop works perfectly if I select a file on the server, for example point to a forest.jpg image (in the public folder). It just refuses to work on preview pictures. I don't want to have to upload the image to crop it, I want to crop based on the preview.

To clarify, in volt:

Volt

in html:

Html

The Controller function is empty, so all that is shown is the view.

Picture.volt


{{ content() }}<br><br>
{{ javascript_include('jcrop/js/jquery.min.js') }}
{{ javascript_include('jcrop/js/jquery.Jcrop.js') }}
{{ javascript_include('jcrop/js/lol.js') }}

{{ stylesheet_link('jcrop/css/jquery.Jcrop.css') }}
{{ stylesheet_link('jcrop/css/lol.css') }}

<div class="bbody">

    <!-- upload form -->
    <form id="upload_form" enctype="multipart/form-data" method="post" files="true" action="upload.php" onsubmit="return checkForm()">
        <!-- hidden crop params -->
        <input type="hidden" id="x1" name="x1" />
        <input type="hidden" id="y1" name="y1" />
        <input type="hidden" id="x2" name="x2" />
        <input type="hidden" id="y2" name="y2" />

        <h2>Step1: Please select image file</h2>
        <div><input type="file" name="image_file" id="image_file" onchange="fileSelectHandler()" /></div>

        <div class="error"></div>

        <div class="step2">
            <h2>Step2: Please select a crop region</h2>
            <img id="preview" />

            <div class="info">
                <label>File size</label> <input type="text" id="filesize" name="filesize" />
                <label>Type</label> <input type="text" id="filetype" name="filetype" />
                <label>Image dimension</label> <input type="text" id="filedim" name="filedim" />
                <label>W</label> <input type="text" id="w" name="w" />
                <label>H</label> <input type="text" id="h" name="h" />
            </div>

            <input type="submit" value="Upload" />
        </div>
    </form>
</div>

lol.js


// convert bytes into friendly format
function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};

// check for selected crop region
function checkForm() {
    if (parseInt($('#w').val())) return true;
    $('.error').html('Please select a crop region and then press Upload').show();
    return false;
};

// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
    $('#x1').val(e.x);
    $('#y1').val(e.y);
    $('#x2').val(e.x2);
    $('#y2').val(e.y2);
    $('#w').val(e.w);
    $('#h').val(e.h);
};

// clear info by cropping (onRelease event handler)
function clearInfo() {
    $('.info #w').val('');
    $('.info #h').val('');
};

// Create variables (in this scope) to hold the Jcrop API and image size
var jcrop_api, boundx, boundy;

var img = document.getElementById('line'); 
var width = 0;
//or however you get a handle to the IMG
var width = img.clientWidth;

function fileSelectHandler() {

    // get selected file
    var oFile = $('#image_file')[0].files[0];

    // hide all errors
    $('.error').hide();

    // check for image type (jpg and png are allowed)
    var rFilter = /^(image\/jpeg|image\/png)$/i;
    if (! rFilter.test(oFile.type)) {
        $('.error').html('Please select a valid image file (jpg and png are allowed)').show();
        return;
    }

    // check for file size
    if (oFile.size > 250 * 1024) {
        $('.error').html('You have selected too big file, please select a one smaller image file').show();
        return;
    }

    // preview element
    var oImage = document.getElementById('preview');

    // prepare HTML5 FileReader
    var oReader = new FileReader();
    oReader.onload = function(e) {

        // e.target.result contains the DataURL which we can use as a source of the image
        oImage.src = e.target.result;
        oImage.onload = function () { // onload event handler

            // display step 2
            $('.step2').fadeIn(500);

            // display some basic image info
            var sResultFileSize = bytesToSize(oFile.size);
            $('#filesize').val(sResultFileSize);
            $('#filetype').val(oFile.type);
            $('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);

            // destroy Jcrop if it is existed
            if (typeof jcrop_api != 'undefined') {
                jcrop_api.destroy();
                jcrop_api = null;
                $('#preview').width(oImage.naturalWidth);
                $('#preview').height(oImage.naturalHeight);
            }

            setTimeout(function(){
                // initialize Jcrop
                $('#preview').Jcrop({
                    minSize: [150, 150], // min crop size
                    aspectRatio : 1, // keep aspect ratio 1:1
                    bgFade: true, // use fade effect
                    bgOpacity: .3, // fade opacity         
                    boxWidth: width,

                    onChange: updateInfo,
                    onSelect: updateInfo,
                    onRelease: clearInfo
                }, function(){

                    // use the Jcrop API to get the real image size
                    var bounds = this.getBounds();
                    boundx = bounds[0];
                    boundy = bounds[1];

                    // Store the Jcrop API in the jcrop_api variable
                    jcrop_api = this;
                });
            },3000);

        };
    };

    // read selected file as DataURL
    oReader.readAsDataURL(oFile);
}

Why won't it work in volt? I've tried with simpler code and it refuses to execute the Jcrop declaration inside the Timeout function...

Thank you!!!



2.7k
edited Jun '15

Please fix the code part of the post. It's kinda messed up.

Could we please see the ouput source code you get from Volt (the page source code).

Did you check for any Javascript errors using the browser's console? (Or any other console add-on/extension like Firebug.)

Fixed it, sorry for that.

Here is the source code of the volt:


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>INVO | Profile</title>
        <link rel="stylesheet" type="text/css" href="/hangstr/css/bootstrap.min.css" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Your invoices">
        <meta name="author" content="Phalcon Team">
    </head>
    <body>
        <nav class="navbar navbar-default navbar-inverse" role="navigation">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">INVO</a>
        </div>
        <div class="nav-collapse"><ul class="nav navbar-nav navbar-left"><li><a href="/hangstr/index/index">Home</a></li><li><a href="/hangstr/invoices/index">Invoices</a></li><li><a href="/hangstr/about/index">About</a></li><li><a href="/hangstr/contact/index">Contact</a></li><li class="active"><a href="/hangstr/profile/show">Show Profile</a></li></ul></div><div class="nav-collapse"><ul class="nav navbar-nav navbar-right"><li class="active"><a href="/hangstr/profile/edit">Edit Profile</a></li><li><a href="/hangstr/session/end">Log Out</a></li></ul></div>    </div>
</nav>

<div class="container">
        <br><br>
<script type="text/javascript" src="/hangstr/jcrop/js/jquery.min.js"></script>
<script type="text/javascript" src="/hangstr/jcrop/js/jquery.Jcrop.js"></script>
<script type="text/javascript" src="/hangstr/jcrop/js/lol.js"></script>

<link rel="stylesheet" type="text/css" href="/hangstr/jcrop/css/jquery.Jcrop.css" />
<link rel="stylesheet" type="text/css" href="/hangstr/jcrop/css/lol.css" />
<div class="bbody">

    <!-- upload form -->
    <form id="upload_form" enctype="multipart/form-data" method="post" files="true" action="upload.php" onsubmit="return checkForm()">
        <!-- hidden crop params -->
        <input type="hidden" id="x1" name="x1" />
        <input type="hidden" id="y1" name="y1" />
        <input type="hidden" id="x2" name="x2" />
        <input type="hidden" id="y2" name="y2" />

        <h2>Step1: Please select image file</h2>
        <div><input type="file" name="image_file" id="image_file" onchange="fileSelectHandler()" /></div>

        <div class="error"></div>

        <div class="step2">
            <h2>Step2: Please select a crop region</h2>
            <img id="preview" />

            <div class="info">
                <label>File size</label> <input type="text" id="filesize" name="filesize" />
                <label>Type</label> <input type="text" id="filetype" name="filetype" />
                <label>Image dimension</label> <input type="text" id="filedim" name="filedim" />
                <label>W</label> <input type="text" id="w" name="w" />
                <label>H</label> <input type="text" id="h" name="h" />
            </div>

            <input type="submit" value="Upload" />
        </div>
    </form>
</div>    <hr>
    <footer>
        <p>&copy; Company 2014</p>
    </footer>
</div>
        <script type="text/javascript" src="/hangstr/js/jquery.min.js"></script>
        <script type="text/javascript" src="/hangstr/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="/hangstr/js/utils.js"></script>
    </body>
</html>


2.7k

You have jquery.min.js included two times. Just pointing that out. Sometimes doing this without doing the jQuery.noConflict() can cause conflicts, even if they are the same version. Besides this the HTML code looks good. I'm looking carefully at the code to see if I notice any other problems.

edited Jun '15

Thanks SparoHawk, I noticed that so I played around with it a bit.

The preview only works if I delete js-include the index.php file and include it in the volt file. If I include the js file in the index and not in the volt, it doesn't work :O

If I include both, then the


{{ image('lol.jpg', 'id': 'target', 'name': 'target')}}

works with Jcrop but not the preview.

If I use


{{ javascript_include('jcrop/js/jquery.min.js') }}

only in index.php, neither works. Maybe this is because in index.php js include is loaded after contect?

index.php


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        {{ get_title() }}
        {{ stylesheet_link('css/bootstrap.min.css') }}
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Your invoices">
        <meta name="author" content="Phalcon Team">
    </head>
    <body>
        {{ content() }}
        {{ javascript_include('jcrop/js/jquery.min.js') }}
        {{ javascript_include('js/bootstrap.min.js') }}
        {{ javascript_include('js/utils.js') }}
    </body>
</html>

Thanks once again!



11.7k
Accepted
answer
edited Jun '15

I tried:

index.php


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        {{ get_title() }}
        {{ stylesheet_link('css/bootstrap.min.css') }}
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Your invoices">
        <meta name="author" content="Phalcon Team">
    </head>
    <body>
        {{ javascript_include('jcrop/js/jquery.min.js') }}
        {{ javascript_include('js/bootstrap.min.js') }}
        {{ javascript_include('js/utils.js') }}
        {{ content() }}
    </body>
</html>

Basically just moving {{ content() }} down three steps.

And now it works without including jquery in the volt file. I guess it requires the Javascript files to be loaded before the contect. I'll go with this for now.

Thank you!



2.7k

Ah. When working with jQuery I always use the domContentLoaded way.

$(function(){

// Your code here

});

This helps avoid problems if your files are included before or after the place where you need them to act.