Not a single line of PHP, but perhaps it's still ok to post here. Unless it should be moved to clientside... Just thought I'd get some feedback for this plugin.

The plugin is supposed to handle dropping files or click + select from file dialog. Can deal with multiple files, but html 5 only, no flash fallback. It uses jQuery.ajax as default method for dealing with dropped/selected files. But it's possible to specify some other handler, if you for example want to use the file data in the browser rather than upload it somewhere.

(function($) {
	/* ajaxSettings will extend jquery ajax settings object
			defaults come from form controls and form attributes such as
			form action and form post
		uploaderSettings extend settings for the uploader functionality
		uploaderSettings are merged into the settings object under settings.uploader below
	*/
	$.fn.uploader = function(ajaxSettings, uploaderSettings) {
		return  this.each(function() {
			 // only invoke the jquery constructor, $(this), once
			var $this = $(this);

		var settings = $.extend({
			'action'	: this.action,
			'method'	: 'post',
			'uploader'	: $.extend({
					'name'		: $this.children('#fileinput').attr('name'),
					'revertText'	: $this.children('#file_multidrop').html(),
					'uploadText'	: 'Uploading…',
					'failText'		: 'Upload failed',
					'uploadClass'	: 'uploading',
					/* default upload handler: contains call to jQuery.ajax */
					'uploadHandler'	: sendFiles,
					'success'		: function(d, t, j) { /* if needed */ },
					'fail'			: function(j, t, e) { /* if needed */ }
				},
				uploaderSettings
			),
			/* jquery ajax.done and ajax.fail may be overridden.
				but generally you'd instead specify handlers in the uploaderSettings
				object, which will then be called from the default ajax handlers
			*/
			'success'	: _success,
			'fail'		: _fail
		}, ajaxSettings);

		var that = this;
		function _success(data, txt, jqx) {
			$f = $('#file_multidrop');
			$f.html(settings.uploader.revertText);
			$f.removeClass(settings.uploader.uploadClass);
			settings.uploader.success(data, txt, jqx);
		}
		function _fail(jqx, txt, err) {
			$f = $('#file_multidrop');
			$f.html(settings.uploader.failText);
			$f.removeClass(settings.uploader.uploadClass);
			settings.uploader.fail(data, txt, jqx);
		}


		$this.children('#file_multidrop').bind('dragenter.uploader', null, false);
		$this.children('#file_multidrop').bind('dragexit.uploader', null, false);
		$this.children('#file_multidrop').bind('dragover.uploader', null, false);

		/*	The user selected files are passed slightly differently from file input and
			FileReader. We need consistent access to those files and that's what we fix
			in the next two event handlers (evt.target.files vs evt.originalEvent.dataTransfer) */
		$this.children('#fileinput').bind('change.uploader', null, function(evt) {
			evt.stopPropagation();
			evt.preventDefault();
			//	The event object for changes to file input contains target.files while (see next comment)...
			settings.fileholder = evt.target;
			settings.uploader.uploadHandler(settings, $(evt.target).parent('form'));
		});
		$this.children('#file_multidrop').bind('click.uploader', null, function(evt) {
			$(this).parent('form').children('#fileinput').trigger('click');
		});
		$this.children('#file_multidrop').bind('drop.uploader', null, function(evt) {
			evt.stopPropagation();
			evt.preventDefault();
			// ... the event object for a drop event contains originalEvent.dataTransfer
			settings.fileholder = evt.originalEvent.dataTransfer;
			settings.uploader.uploadHandler(settings, $(evt.target).parent('form'));
		});
	});

	function sendFiles(settings, form) {
		if (settings.fileholder.files.length)
		{
			var fd = new FormData();
			for (var i = 0; i < settings.fileholder.files.length; ++i) {
				var f = settings.fileholder.files[i];
				fd.append(settings.uploader.name+'['+i+']', f);
			}
			for (x in settings.data) {
				fd.append(x, settings.data[x]);
			}

			/* contentType must be false or the boundary attribute will be overwritten */
			settings['contentType'] = false;
			/* processData must be false or fd (FormData) will be transformed to string */
			settings['processData'] = false;

			var fileHandlerOptions = {
				'url'	: settings.action,
				'type'	: settings.method,
				/* (unverified) I read somewhere that contentType and processData...
				/* ... must be false or the boundary attribute will be overwritten */
				'contentType': false,
				/* ... must be false or fd (FormData) will be transformed to string */
				'processData': false,
				'data'	: fd,
				'cache'	: false,
				'dataType'	: 'json',

				'beforeSend'	: function() {
					$('#file_multidrop').html(settings.uploader.uploadText);
					$('#file_multidrop').addClass(settings.uploader.uploadClass);
				}
			}
			$.ajax(fileHandlerOptions).done(function (data, txt, jqx) {
					settings.success(data, txt, jqx);
				}
			).fail(function (jqx, txt, err) {
					settings.fail(jqx, txt, err);
				}
			);
		}
	}
};
})(jQuery);

Setting it up

/*	The first argument specifies settings used for jQuery.ajax settings which may override
	stuff specified by form attributes etc, such as form action.
	Success and failure handlers may be overridden, but it's recommended that you specify
	them in the second argument instead.
 *	The second argument specifies settings regarding the uploader: texts and css class to use when uploading,
 	success and failure handlers specified here will be called in ajax.done and ajax.fail
 */
jQuery(document).ready(function($) {
	$('#uploadform').uploader({},
		{
			/* Texts to display after upload / waiting for more files. Only needed if you don't
				want to use the default text as specified inside the #file_multidrop div */
			'revertText' : 'Done',
			/* Text to display when uploading */
			'uploadText' : 'Uploading…',
			/* CSS class to add when uploading, for example to highlight button */
			'uploadClass': 'uploading'
			/* specify success handler if needed */
			,'success'	: foo_success
			/* specify fail handler if needed */
			,'fail'		: foo_fail
			/* If you want to use some other handler for files than the default function sendFiles */
			, 'uploadHandler'	: foo_handlefiles
		}
	);
});

Used together with

	<!-- form's action attribute: default action for uploads -->
    <form id="uploadform" action="/a_test.php" method="post">
       	<!-- use whatever class you want for styling purposes. it is not used by the plugin -->
       	<div id="file_multidrop" class="file-button">Drop files here or click button</div>
    	<!-- file input name attribute used as default for file uploads ($_FILES['name_attribute_value']) -->
    	<input type="file" name="FileData[]" id="fileinput" multiple style="display:none;">
	</form>

And do note that you do not have to upload files at all. You may continue to use them by specifying uploadHandler, such as

function foo_handleFile(opts, frm) {
	// Only deals with the first file
	var f = opts.fileholder.files[0];
	var fr = new FileReader();
	fr.onload = function() { alert('file loaded'); };
	fr.readAsBinaryString(f);
}

    For what its worth, you should use a blank object as the first parameter to extend, that way if you use this twice, you haven't overwritten your default settings. IE:

    var defaults = {
       something: 'value',
       else: 'another'
       // etc
    };
    
    this.options = $.extend({},defaults,input);
    
      19 days later

      Thanks Derokorian. That looks like the kind of misstake that takes a lot of time to find when the problem suddenly comes into play.

        Write a Reply...