Monthly Archives: September 2013

Fine Uploader 3.9 Released

Update: Oct 9, 2013 – 3.9.1 Hotfix Release

This hotfix release addresses the following issue/workflows:

  • Bucket name determination does not handle underscores correctly. (#1014)

Overview

Fine Uploader 3.9 is released!

Any integrators who have to support iOS users will be pleased to hear that Safari on iOS7 is supported by Fine Uploader. This means you can roll out a new version of Fine Uploader and be sure that nothing will change for your iOS users when the latest update is released later this week. Please note that there is a bug affecting Fine Uploader in iOS7 that Apple has yet to address. It is described more in issue #990, along with a workaround that you will need to utilize if you plan to allow your users to upload MOV files via iOS.

We’ve also added native support for multiple file input elements. This means advanced integrators can have custom styles, attributes, and options for forms that require more than one of these types of elements. This also eliminates the need for multiple Fine Uploader instances.

Also in the scope of UI is the added option to return a qq.Promise() instance from the showPrompt and showMessage functions. Read more about promises, or read a tutorial on integrating third-party dialog libraries.

Users of the S3 mode will appreciate that you can now send custom headers along with all S3 AJAX requests to your origin server. The failure handling logic associated with upload success requests sent to your local server after a file is in S3 has also been improved quite a bit.

Features and Enhancements

  • Add iOS7 Safari support (#989)
  • showPrompt and showConfirm functions are optionally promissory (#719)
  • Improved uploadSuccess request failure logic (#974)
  • Add native support for multiple upload buttons (#819)
  • Allow custom parameters to be passed along with the uploadSuccess request (#965)
  • Allow headers to be specified for all S3 AJAX requests to origin server (#956)

Bugs Fixed

  • Improved legacy browser JSON response parsing for CORS environments (#913)
  • paste.promptForName option fix; was not prompting user for pasted filename (#987)
  • Delete, cancel, and edit filename UI features not working after resetting a Fine Uploader instance (#963)
  • Reset function throws an exception if the dropzone is disabled (#953)
  • validate event handler is passed the File input element instead of file metadata (#993)

What’s Next

The next version of Fine Uploader will likely be 4.0 and contain breaking changes. Our plans are to improve the user interface even more. 4.0 will include native support for image previews, a complete redesign of templating in Fine Uploader UI to make the experience of designing and integrating Fine Uploader into an existing site less painful, and fineuploader.com enhancements.

As usual, if you have any new ideas or want to weigh in with your thoughts on any existing ideas then find us on the issue tracker. At least two of the features that made it to this release were directly requested by users of Fine Uploader; proof that we listen to your feedback!

– Ray Nicholus, Mark Feltner, and the rest of the team at Widen

Alertify Your Notifications and Dialogs

This is the first in a series of planned blog posts which will demonstrate how to integrate Fine Uploader along with various external libraries. If you would like to a specific library featured, then open up a feature request with reasons why you think it’d be awesome.

NOTE: This post assumes you are using Fine Uploader 4.x, as some of the syntax, particularly related to templates, changed (for the better) in 4.0. Please see the documentation site for more information.


Today, I’ll demonstrate how you can add a bit of spiff to Fine Uploader’s default UI by overriding the default dialog windows and adding notifications on certain events. Hopefully after reading this, it’ll be simple for you to get your uploader UI from looking like this:

bad-alert-2

To looking like this:

fine-message

We’ll use the alertify.js library to do most of the work for us. Also, we’ll use the latest version of Bootstrap to show how easy it is integrate and really make the uploader a cynosure.

Page Setup

First thing is first, acquire Fine Uploader, and ensure to include it on your page. The following HTML page should serve as a good starting point:

index.html

<!DOCTYPE html>
<html>
    <head>
        <!-- STYLESHEETS
        ========== -->
        <link rel="stylesheet"
              href="assets/vendor/fineuploader-4.0.0/custom.fineuploader-4.0.0.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.core.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.default.css">
        <link rel="stylesheet" href="assets/css/style.css">

    <!-- TEMPLATE
    ========== -->
    <script type="text/template" id="qq-template">
        <div class="qq-uploader-selector qq-uploader">
            <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
                <span>Drop files here to upload</span>
            </div>
            <div class="qq-upload-button-selector qq-upload-button">
                <div>Upload a file</div>
            </div>
            <span class="qq-drop-processing-selector qq-drop-processing">
                <span>Processing dropped files...</span>
                <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
            </span>
            <ul class="qq-upload-list-selector qq-upload-list">
                <li>
                    <div class="qq-progress-bar-container-selector">
                        <div class="qq-progress-bar-selector qq-progress-bar"></div>
                    </div>
                    <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                    <span class="qq-edit-filename-icon-selector qq-edit-filename-icon"></span>
                    <span class="qq-upload-file-selector qq-upload-file"></span>
                    <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
                    <span class="qq-upload-size-selector qq-upload-size"></span>
                    <a class="qq-upload-cancel-selector qq-upload-cancel" href="#">Cancel</a>
                    <a class="qq-upload-retry-selector qq-upload-retry" href="#">Retry</a>
                    <a class="qq-upload-delete-selector qq-upload-delete" href="#">Delete</a>
                    <span class="qq-upload-status-text-selector qq-upload-status-text"></span>
                </li>
            </ul>
        </div>
    </script>
    </head>
    <body>

        <div class="container">
            <div id="uploader">
            </div>
        </div> <!-- /container -->

        <!-- JAVASCRIPTS
        ========== -->
        <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.min.js"></script>
        <script src="assets/vendor/fineuploader-4.0.0/custom.fineuploader-4.0.0.js"></script>
        <script src="assets/js/main.js"></script>

    </body>
</html>

main.js

var options = {
  element: $("#uploader"),
  request: {
    endpoint: '/upload/receiver'
  },
  showMessage: function(message) {},
    showConfirm: function(message) {},
  showPrompt: function(message, defaultValue) {}
};

$(document).ready(function() {
    alertify.set({ delay: 10000 }); // delay log alerts for 10 seconds

    $("#uploader").fineUploader(options);
  });

Fine Uploader UI mode, by default, displays three kinds of modal dialogs: prompts, messages, and confirms. These dialogs are implemented with window.prompt, window.alert, and window.confirm, respectively. If you have ever seen these dialogs you know their appearance is unprofessional and implemented differently on different browsers. With alertify, Fine Uploader, and a few lines of code we can unify the appearance of dialogs and improve their look and feel.

Notification Station

Users like it when the page responds to their actions with visual cues. Let’s say we would like to show a notification to the user when an upload completes or fails, and if it fails we want the reason to be shown to the user. With Fine Uploader’s events we can hook in almost any logic we want at the moment something happens (whatever that something may be). For this, we’ll need to provide custom callbacks that will display a proper message for the complete and error events.

$("#uploader")
    .on('complete', function (event, fileId, fileName, responseJSON, xhr) {
        if (responseJSON.success === true) {
            alertify.success("Successfully uploaded: " + fileName, "", 0);
        } else {
            alertify.error("Error: " + error);
        }               
    });

Now, when a user successfully uploads a file they’ll get a notification like so:

success-notification

And when the uploader has a problem uploading, there will be a notification message showing exactly what went wrong:

failure-notification

‘Alert!’

Fine Uploader UI will issue a window.alert the moment one of its validators detects some sort of issue with a file that has been submitted.

Before we override this show method, let’s set up a simple validator that will stop the user from uploading more than 3 files. (Note that you can set up all sorts of validators see the docs, but this one is kept trivial for demonstration purposes.

var options = {
  element: $("#uploader"),
  request: {
    endpoint: '/upload/receiver'
  },
  validation: {
      itemLimit: 3 // Disallow >3 submitted files
  },
  showMessage: function(message) {},
  showConfirm: function(message) {},
  showPrompt: function(message, defaultValue) {}
};

Alright, so now when a user drops 4 or more files a window.alert will pop up like so:

bad-alert-2

That’s not pretty, and we want pretty. Provide a new callback to the showMessage option which will instead display our own message via alertify:

showMessage: function (message) {
    return alertify.alert(message);
},

fine-message

Woah, that is hella better. It’s like a breath of fresh air. Onward!

Please confirm …

Sometimes Fine Uploader UI needs confirmation from the user that it is safe to continue with the operation it is about to perform. For example, if the deleteFile.confirmMessage option is true then Fine Uploader will display a confirmation dialog to the user to ensure that it is safe to delete the corresponding file.

First, enable the delete feature in Fine Uploader and on your server:

var options = {
  element: $("#uploader"),
  request: {
    endpoint: '/upload/receiver'
  },
  validation: {
      itemLimit: 3
  },
  deleteFile: {
      enabled: true, // turn the delete file feature on
      forceConfirm: true // enable FU confirming with the user that they want to proceed with delete
  },  
  showMessage: function (message) {
      return alertify.alert(message);
  },
  showConfirm: function(message) {},
  showPrompt: function(message, defaultValue) {}
};

Now, when the user presses ‘Delete’ next to a successfully uploaded file, they’ll see a confirmation dialog:

bad-confirm

We can completely customize this dialog very easily with alertify. All we have to do is provide a promissory function as the value of the showConfirm option.

showConfirm: function (message) {
    var promise;
    promise = new qq.Promise();
    alertify.confirm(message, function(result) {
      if (result) {
        return promise.success(result);
      } else {
        return promise.failure();
      }
    });
    return promise; 
}

And with 10 lines of code, we have a spiffy looking confirmation dialog (well, at least one that’s waaay better than window.confirm).

fine-confirm

PRO-mpting

The last dialog function in Fine Uploader’s repertoire is showPrompt.
A prompt is useful when you want to query the user for some information (e.g., a custom filename). There is only one instance where Fine Uploader makes use of prompts, but it’ s a necessary one. Fine Uploader has support for submitting images via the ClipboardAPI. The ClipboardAPI is young, though, and does not yet have the ability to grab the filename from the submitted file. If paste is enabled in Fine Uploader, and promptForName is true then the user will be prompted for a filename to enter before Fine Uploader begins processing.

So first, enable the paste feature:

var options = {
  element: $("#uploader"),
  request: {
    endpoint: '/upload/receiver'
  },
  validation: {
      itemLimit: 3
  },
  deleteFile: {
      forceConfirm: true,

  },  
  paste: {
      promptForName: true, // enabling prompting for filename on paste received
      targetElement $(document) // set the paste target element to be the entire web page
  },
  showMessage: function (message) {
    return alertify.alert(message);
  },
  showConfirm: function (message) {
    var promise;
    promise = new qq.Promise();
    alertify.confirm(message, function(result) {
      if (result) {
        return promise.success(result);
      } else {
        return promise.failure();
      }
    });
    return promise; 
  },
  showPrompt: function(message, defaultValue) {},
};

bad-prompt

showPrompt: function(message, defaultValue) {
  promise = new qq.Promise()
  alertify.prompt(message, (result, inStr) ->
    if result
      promise.success(inStr)
    else
      promise.failure(inStr)
  , defaultValue)
  return promise
},

fine-prompt

Making Promises

You may have noticed the use of the qq.Promise() throughout this code. When showConfirm or showPrompt return a qq.Promise() Fine Uploader will internally hold of on executing code until immediately after the promise is fulfilled.

For example, showConfirm will be called the moment the user clicks the “delete” button for an uploaded file. If the promise is successful (i.e., promise.success() fulfills the promise), then the delete request is submitted. If the promise is a failure (i.e., promise.failure() fulfills the promise), then the delete request is not submitted.

This is a huge win because the UI thread will continue running while the app is waiting for the user’s confirmation.

This Time With More Style

Why stop now. Let’s keep adding some style tweaks (mostly because they bug me). Throw Bootstrap in there! Hack some CSS! Have some fun! I’ve provided the complete example I used to cook up this tutorial below, hopefully someone finds it useful.

First, add bootstrap to your already set up page:

<link rel="stylesheet"
      href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0-rc2/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/js/bootstrap.min.js"></script>

Include a modified template in your document, based off of the bundled default template:

<script type="text/template" id="qq-template">
    <div class="qq-uploader-selector qq-uploader col-lg-12">
        <pre class="qq-upload-drop-area-selector qq-upload-drop-area col-lg-12" qq-hide-dropzone>
            <span>Drop files here to upload</span>
        </pre>
        <div class="qq-upload-button-selector btn btn-success">
            <div>Upload a file</div>
        </div>
        <span class="qq-drop-processing-selector qq-drop-processing">
            <span>Processing dropped files...</span>
            <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
        </span>
        <ul class="qq-upload-list-selector qq-upload-list list-group">
            <li>
                <div class="qq-progress-bar-container-selector progress">
                    <div class="qq-progress-bar-selector bar"></div>
                </div>
                <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                <span class="qq-upload-file-selector qq-upload-file"></span>
                <span class="qq-upload-size-selector qq-upload-size"></span>
                <a class="qq-upload-delete-selector btn" href="#">Delete</a>
                <span class="qq-upload-status-text-selector qq-upload-status-text"></span>
            </li>
        </ul>
    </div>
</script>

Add some custom styles to your own stylesheet:

style.css

.qq-upload-list {
  margin-top: 7px;
  text-align: left;
}
.qq-upload-list > li {
  margin-top: 3px;
}
.qq-drop-area {
  min-height: 200px;
}
.alert-error .qq-upload-failed-text {
  display: inline;
}

And add the corresponding classes to Fine Uploader’s classes option:

classes: {
    success: 'alert alert-success list-group-item',
    fail: 'alert alert-error list-group-item'
}

We can also modify many of the messages that are used in the prompts we’ve customized. Let’s change the default delete message by changing the confirmMessage property on the delete option we created earlier:

deleteFile: {
  enabled: true,
  confirmMessage: 'Send <code>{filename}</code>; into the abyss?',
  forceConfirm: true
},

The Final Product

This is probably what you’ve been waiting for. Below is the entire HTML, CSS, JS code that I used to create everything above. Happy hacking!

index.html

<!DOCTYPE html>
<html>
    <head>
        <!-- STYLESHEETS
        ========== -->
        <link rel="stylesheet"
              href="assets/vendor/fineuploader-4.0.0/custom.fineuploader-4.0.0.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0-rc2/css/bootstrap.min.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.core.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.default.css">
        <style>
            body {
                padding-top: 60px;
                padding-bottom: 40px;
            }
        </style>
        <link rel="stylesheet" href="assets/css/style.css">

        <script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
    <script type="text/template" id="qq-template">
        <div class="qq-uploader-selector qq-uploader col-lg-12">
            <pre class="qq-upload-drop-area-selector qq-upload-drop-area col-lg-12" qq-hide-dropzone>
                <span>Drop files here to upload</span>
            </pre>
            <div class="qq-upload-button-selector btn btn-success">
                <div>Upload</div>
            </div>
            <span class="qq-drop-processing-selector qq-drop-processing">
                <span>Processing dropped files...</span>
                <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
            </span>
            <ul class="qq-upload-list-selector qq-upload-list list-group">
                <li>
                    <div class="qq-progress-bar-container-selector progress">
                        <div class="qq-progress-bar-selector bar"></div>
                    </div>
                    <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                    <span class="qq-upload-file-selector qq-upload-file"></span>
                    <span class="qq-upload-size-selector qq-upload-size"></span>
                    <a class="qq-upload-delete-selector btn" href="#">Delete</a>
                    <span class="qq-upload-status-text-selector qq-upload-status-text"></span>
                </li>
            </ul>
        </div>
    </script>
    </head>
    <body>

        <div class="container">
            <div id="uploader">
            </div>
        </div> <!-- /container -->

        <!-- JAVASCRIPTS
        ========== -->
        <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/js/bootstrap.min.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/alertify.js/0.3.10/alertify.min.js"></script>
        <!--
        <script src="assets/vendor/bootbox.min.js"></script>
        -->
        <script src="assets/vendor/fineuploader-4.0.0/custom.fineuploader-4.0.0.js"></script>
        <script src="assets/js/main.js"></script>

    </body>
</html>

style.css

.qq-upload-list {
  margin-top: 7px;
  text-align: left;
}
.qq-upload-list > li {
  margin-top: 3px;
}
.qq-drop-area {
  min-height: 200px;
}
.alert-error .qq-upload-failed-text {
  display: inline;
}

main.js

  var options = {
    debug: true,
    element: $("#uploader"),
    deleteFile: {
      enabled: true,
      confirmMessage: 'Send <code>{filename}</code> into the abyss?',
      forceConfirm: true
    },
    template: template,
    classes: {
      success: 'alert alert-success list-group-item',
      fail: 'alert alert-error list-group-item'
    },
    validation: {
      itemLimit: 3
    },
    paste: {
      promptForName: true,
      targetElement: $(window.document)
    },
    showMessage: function(message) {
      return alertify.alert(message);
    },
    showPrompt: function(message, defaultValue) {
      var promise;
      promise = new qq.Promise();
      alertify.prompt(message, function(result, inStr) {
        if (result) {
          return promise.success(inStr);
        } else {
          return promise.failure(inStr);
        }
      }, defaultValue);
      return promise;
    },
    showConfirm: function(message) {
      var promise;
      promise = new qq.Promise();
      alertify.confirm(message, function(result) {
        if (result) {
          return promise.success(result);
        } else {
          return promise.failure();
        }
      });
      return promise;
    }
  };

  $(document).ready(function() {
    alertify.set({
      delay: 10000
    });
    return $("#uploader").fineUploader(options).on('complete', function(event, fileId, fileName, responseJSON, xhr) {
      if (responseJSON.success != null) {
        return alertify.success("Successfully uploaded: <code>" + fileName + "</code>", "", 0);
      }
    }).on('error', function(event, fileId, fileName, error, xhr) {
      return alertify.error("Error: " + error);
    });
  });

Other Libraries

Of course, we aren’t limited to using alertify, we could hook in any other sort of prompting library we wanted. Other alternatives include Bootbox.js, Bootstrap’s modal.js, and much more.

If you think you’ve created something neat, share it on GitHub and/or send us a screenshot, we’re always looking for ideas and cutting-edge uses of Fine Uploader.