# File Input

### 1. Custom File Input

- A simple file input control which styles browsers default input and adds some extra features
- If you want advanced functionality, you should consider using dropzone.js or other library.
- Basic usage is:

       $('#my-file-input').ace_file_input();

### 2. Options

- You can use one of the following options:
  1. `style` default=false. If you set it to well, the large style will be displayed
  2. `no_file` the text to show when no file is selected
  3. `no_icon` the icon to show when no file is selected
  4. `btn_choose` button text when no file is selected
  5. `btn_change` button text when a file is selected
  6. `icon_remove` icon to use for remove(reset) button
  7. `droppable` default=false. set true to enable drag & drop
  8. `thumbnail` small, large or fit. Used when style options is well
  10. `allowExt` a list of allowed extensions (whitelist)
  11. `denyExt` a list of denied extensions (blacklist)
  12. `allowMime` a list of allowed mime types (whitelist)
  13. `denyMime` a list of denied mime types (blacklist)
  14. `maxSize` maximum file size allowed in bytes
  The above allow/deny options, trigger `file.error.ace` event when an invalid file is selected.
  Please refer to "File Filtering" section for more info.

      $('#my-file-input').ace_file_input({
          style: 'well',
          no_file: 'Click to choose or drag & drop',
          droppable: true, //html5 browsers only
          thumbnail: 'small', //html5 browsers only

          maxSize: 100000, //~100 KB
          allowExt:  ['jpg', 'jpeg', 'png', 'gif', 'tif', 'tiff', 'bmp'],
          allowMime: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/tif', 'image/tiff', 'image/bmp'] //html5 browsers only
      });

      $('#my-file-input2').ace_file_input({
          denyExt:  ['exe', 'php']
      });

### 3. Callbacks

- There are 3 callback option
- `before_change` callback is called when user selects/drops files and before files are displayed.
  Inside it you can return:
  1. a modified file list
  2. `true` and all files will be kept
  3. `false` and no files will be kept
  4. `-1` and no files will be kept and input will be reset
  Also it accepts following arguments:
  1. `files` a list of selected/dropped files which is a FileList in modern browsers and a string (filename) array for older browsers
  2. `dropped` whether files have been dropped or selected

      $('#my-file-input').ace_file_input({
        ...
        ,
       before_change: function(files, dropped) {
          //files is a "File" object array in modern browsers
          //files is a "string" (file name) array in older browsers

          //dropped=true if files are drag & dropped

          var valid_files = []

          for(var i = 0; i < files.length; i++) {
            var file = files[i];

            if( typeof file === 'string') {
                /older browsers that don't support FileReader API, such as IE
                //here, file is just a filename string
                //if (file is valid) valid_files.push(file);
            } else if( 'File' in window && file instanceof window.File ) {
                //file is a "File" object with following properties
                //file.name
                //file.type (mime type)
                //file.size
                //if (file is valid) valid_files.push(file);
            }
          }

          return valid_files;

          //or
          return true;//original input files

          //or
          return false;//all are invalid, but don't reset input

          //or
          return -1;//all are invalid, reset input
       }

- before_remove can be used to disable resetting file input, for example during a file upload session:

       $('#my-file-input').ace_file_input({
          ...
      	,
          before_remove : function() {
            if( uploadInProgress) return false;//don't allow resetting
            return true;
          }
       });

- If preview_error is defined, it will be called in case there's an error when building preview of image files:

      $('#my-file-input').ace_file_input({
          ...,
          preview_error : function(filename, error_code) {
             //filename = name of the file that had a problem
             switch(error_code) {
                case 1:
                //FILE_LOAD_FAILED
                break;

                case 2:
                //IMAGE_LOAD_FAILED
                break;

                case 3:
                //THUMBNAIL_FAILED
                break;
             }
         }
      });

### 4. File Filtering

- As mentioned in previous sections, you can use the following options and callbacks to filter the list of selected files:
  1. `allowExt`
  2. `denyExt`
  3. `allowMime`
  4. `denyMime`
  5. `maxSize`
  6. `before_change`
- Please note that when you select a file using file dialog, browser's file input element cannot be modified. It can only be reset (emptied).
  For example if you specify `allowExt` option to select only image files, but user selects none-image files as well, the browser keeps all selected files but Ace file input shows image files only.
- When user selects an invalid file `file.error.ace` event is triggered which has some info about the error.
- If you call `preventDefault` on event object, the file control will be reset and becomes empty.
  Otherwise, file control won't be reset and browser keeps all selected files.
  However, Ace file input displays valid files and the `$('#my-file-input').data('ace_input_files')` returns valid files only.

-      $('#my-file-input').ace_file_input({
          'allowExt': ['jpg', 'jpeg', 'png']
       })
       .on('file.error.ace', function(event, info) {
      	//info.file_count > number of files selected
      	//info.invalid_count > number of invalid files

      	//info.error_count['ext'] > number of files with invalid extension (only if allowExt or denyExt is set)
      	//info.error_count['mime'] > number of files with invalid mime type (only if allowMime or denyMime is set)
      	//info.error_count['size'] > number of files with invalid size (only if maxSize option is set)

      	//info.error_list['ext'] > list of file names with invalid extension
      	//info.error_list['mime'] > ...
      	//info.error_list['size'] > ...

      	//info.dropped > true if files have been selected by drag & drop

      	//if you do this
      	event.preventDefault();
      	//it will reset (empty) file input, i.e. no files selected
       });

- There is also `before_change` callback. Please see previous section for more info.

### 5. Functions

- The following functions are available for file input control.
- `show_file_list` takes a list of file names and shows them inside the control:

      $('#my-input').ace_file_input('show_file_list', ['file1.txt']);

  For example, you may want to edit a file input which has user's previous selection.
  From version 1.3.2 you can also preview images inside file input control.

      $('#my-input')
      .ace_file_input('show_file_list', [
          {type: 'image', name: 'name of image', path: 'http://path/to/image/for/preview'},
          {type: 'file', name: 'hello.txt'}
      ]);
      //type can be 'image', 'video', 'audio', 'file'

- `update_settings` to update options:

      $('#my-input').ace_file_input('update_settings', {
         allowExt: ['pdf', 'doc', 'docx']
      });

- `files` returns list of selected files:

      var files = $('#my-input').ace_file_input('files');
      //is similar to:
      var files = $('#my-input').data('ace_input_files');

- `method` returns method used by user which is 'select' or 'drop':

      var method = $('#my-input').ace_file_input('method');
      //is similar to:
      var method = $('#my-input').data('ace_input_method');

- `disable` disables file input:

      $('#my-input').ace_file_input('disable');

- `enable` enables file input:

      $('#my-input').ace_file_input('enable');

- `enable_reset` takes an argument and enables/disables reset button:

      $('#my-input').ace_file_input('enable_reset', false);
      $('#my-input').ace_file_input('enable_reset', true);

- `reset_input` resets file input:

      $('#my-input').ace_file_input('reset_input');

- `reset_input_ui` resets file input's UI only:

      $('#my-input').ace_file_input('reset_input_ui');

  Suppose you have a form with a reset button.
  When you hit "reset", file input field will be reset, but Ace file input's UI won't be reset.
  You should do this on your own:

      $('#my-form').on('reset', function(e) {
          $('#my-input').ace_file_input('reset_input_ui');
      });

- `reset_input_field` resets file input field
- `loading` adds/updates/removes a loading/waiting overlay for example during a file upload:

       $('#my-input').ace_file_input('loading' , true);//adds overlay
       $('#my-input').ace_file_input('loading' , false);//removes overlay
       $('#my-input').ace_file_input('loading' , ' custom content inside overlay such as a progressbar ');

### 6. Events

- If you use file filter options and user selects or drops an invalid file, a `file.error.ace` event is triggered with some info about invalid files.
  You can find more info about it in the File Filtering section.
- You can also add a change event listener to the file input and it will be triggered when files are selected or dropped:

       $('#my-file-input').ace_file_input({
         droppable: true
       })
       .on('change', function() {
          var files = $(this).data('ace_input_files');
          var method = $(this).data('ace_input_method');
       });

### 7. Uploading

- If you don't use drag & drop functionality, file upload is done as normal, by submitting the form and browser takes care of it.
- However when you enable drag & drop feature and user drops some files onto the control, file field is not modified and by submitting form, no files are uploaded.
- In this case, you should use modern HTML5 features and upload your files using Ajax and FormData object.
  FormData
- The list of files which should be uploaded is retrieved like this:

      var files = $('#my-file-input').data('ace_input_files');

- For a working example and more info please see `examples/file-upload.html`.
- When using FormData object if you use the following to create your FormData object, browser will include all the files that are selected by user:

      var myform = $('#myform');
      var fd = new FormData(myform.get(0));//populate FormData with myform data

  This way our filtered files are not taken into account.
  So you can do the following instead:

      var myform = $('#myform');
      var fd = new FormData();//empty FormData object
      $.each(myform.serializeArray(), function(i, item) {
        //add form fields one by one to our FormData
        fd.append(item.name, item.value);
      });

  The above does not add file fields to FormData and we should append our files like this:

      myform.find('input[type=file]').each(function(){
        var field_name = $(this).attr('name');
        //for fields with "multiple" file support
        //field name should be something like `myfile[]`

        var files = $(this).data('ace_input_files');
        if(files && files.length > 0) {
           for(var f = 0; f < files.length; f++) {
             fd.append(field_name, files[f]);
          }
        }
      });

  Now our FormData object can be submitted using Ajax:

      var deferred = $.ajax({
                url: myform.attr('action'),
               type: myform.attr('method'),
        processData: false,//important
        contentType: false,//important
           dataType: 'json',//depending on your server side response
               data: fd//our FormData object
      })

      deferred.done(function(result) {
        //on success (successful response from server)
      }).fail(function(result) {
        //unable to receive a valid response from server
      });

- For older browsers that don't support `FormData` object, we can use an invisible frame to upload our form without leaving or refreshing the page:

      var deferred = new $.Deferred //create a custom deferred object, because there's no ajax here to create it for us
      var temporary_iframe_id = 'temporary-iframe-'+(new Date()).getTime()+'-'+(parseInt(Math.random()*1000));
      var temp_iframe =
           $('<iframe id="'+temporary_iframe_id+'" name="'+temporary_iframe_id+'" \
             frameborder="0" width="0" height="0" src="about:blank"\
             style="position:absolute; z-index:-1; visibility: hidden;" />')
            .insertAfter(myform)

      myform.append('<input type="hidden" name="temporary-iframe-id" value="'+temporary_iframe_id+'" />');

      temp_iframe.data('deferrer' , deferred);
      //we save the deferred object to the iframe and in our server side response
      //we use "temporary-iframe-id" to access iframe and its deferred object

      myform.attr({
             'method': 'POST',
            'enctype': 'multipart/form-data',
             'target': temporary_iframe_id //important
      });

      myform.get(0).submit();

      //if we don't receive a response after 30 seconds, let's declare it as failed!
      ie_timeout = setTimeout(function(){
          ie_timeout = null;
          temp_iframe.attr('src', 'about:blank').remove();
          deferred.reject({'status':'fail', 'message':'Timeout!'});
      } , 30000);
      }

  And deferred callbacks are similar to ajax example.

### 8. Notes

- Please note that validating files inside browser is not secure.
  You should always check uploaded files in your server side script.
  A php example inside `examples/file-upload.php` validates files and only allows image files.
- Most modern browsers allow multiple file selection.
  If you want that, make sure your file input control has `multiple` attribute and input name has brackets like `myfiles[]`:

       <input multiple type="file" name="myfiles[]" id="myfiles" />