/** * jquery.plupload.queue.js * * Copyright 2009, Moxiecode Systems AB * Released under GPL License. * * License: http://www.plupload.com/license * Contributing: http://www.plupload.com/contributing */ /* global jQuery:true, alert:true */ /** jQuery based implementation of the Plupload API - multi-runtime file uploading API. To use the widget you must include _jQuery_. It is not meant to be extended in any way and is provided to be used as it is. @example

Your browser doesn't have Flash, Silverlight or HTML5 support.

@example // Retrieving a reference to plupload.Uploader object var uploader = $('#uploader').pluploadQueue(); uploader.bind('FilesAdded', function() { // Autostart setTimeout(uploader.start, 1); // "detach" from the main thread }); @class pluploadQueue @constructor @param {Object} settings For detailed information about each option check documentation. @param {String} settings.url URL of the server-side upload handler. @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled. @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message. @param {Array} [settings.filters=[]] Set of file type filters, each one defined by hash of title and extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR` @param {String} [settings.flash_swf_url] URL of the Flash swf. @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs. @param {Number|String} [settings.max_file_size] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`. @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event. @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message. @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload. @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog. @param {Boolean} [settings.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess. @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}` @param {Number} [settings.resize.width] If image is bigger, it will be resized. @param {Number} [settings.resize.height] If image is bigger, it will be resized. @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100). @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally. @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails. @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files. @param {Boolean} [settings.dragdrop=true] Enable ability to add file to the queue by drag'n'dropping them from the desktop. @param {Boolean} [settings.rename=false] Enable ability to rename files in the queue. @param {Boolean} [settings.multiple_queues=true] Re-activate the widget after each upload procedure. */ ;(function($, plupload) { var uploaders = {}; function _(str) { return plupload.translate(str) || str; } function renderUI(id, target) { // Remove all existing non plupload items target.contents().each(function(i, node) { node = $(node); if (!node.is('.plupload')) { node.remove(); } }); target.prepend( '
' + '
' + '
' + '
' + '
' + '
' + _('Select files') + '
' + '
' + _('Add files to the upload queue and click the start button.') + '
' + '
' + '
' + '
' + '
' + '
' + _('Filename') + '
' + '
 
' + '
' + _('Status') + '
' + '
' + _('Size') + '
' + '
 
' + '
' + '
    ' + '' + '
    ' + '
    ' + '
    ' + '' + '
    ' ); } $.fn.pluploadQueue = function(settings) { if (settings) { this.each(function() { var uploader, target, id, contents_bak; target = $(this); id = target.attr('id'); if (!id) { id = plupload.guid(); target.attr('id', id); } contents_bak = target.html(); renderUI(id, target); settings = $.extend({ dragdrop : true, browse_button : id + '_browse', container : id }, settings); // Enable drag/drop (see PostInit handler as well) if (settings.dragdrop) { settings.drop_element = id + '_filelist'; } uploader = new plupload.Uploader(settings); uploaders[id] = uploader; function handleStatus(file) { var actionClass; if (file.status == plupload.DONE) { actionClass = 'plupload_done'; } if (file.status == plupload.FAILED) { actionClass = 'plupload_failed'; } if (file.status == plupload.QUEUED) { actionClass = 'plupload_delete'; } if (file.status == plupload.UPLOADING) { actionClass = 'plupload_uploading'; } var icon = $('#' + file.id).attr('class', actionClass).find('a').css('display', 'block'); if (file.hint) { icon.attr('title', file.hint); } } function updateTotalProgress() { $('span.plupload_total_status', target).html(uploader.total.percent + '%'); $('div.plupload_progress_bar', target).css('width', uploader.total.percent + '%'); $('span.plupload_upload_status', target).html( plupload.sprintf(_('Uploaded %d/%d files'), uploader.total.uploaded, uploader.files.length) ); } function updateList() { var fileList = $('ul.plupload_filelist', target).html(''), inputCount = 0, inputHTML; $.each(uploader.files, function(i, file) { inputHTML = ''; if (file.status == plupload.DONE) { if (file.target_name) { inputHTML += ''; } inputHTML += ''; inputHTML += ''; inputCount++; $('#' + id + '_count').val(inputCount); } fileList.append( '
  • ' + '
    ' + file.name + '
    ' + '
    ' + '
    ' + file.percent + '%
    ' + '
    ' + plupload.formatSize(file.size) + '
    ' + '
     
    ' + inputHTML + '
  • ' ); handleStatus(file); $('#' + file.id + '.plupload_delete a').click(function(e) { $('#' + file.id).remove(); uploader.removeFile(file); e.preventDefault(); }); }); $('span.plupload_total_file_size', target).html(plupload.formatSize(uploader.total.size)); if (uploader.total.queued === 0) { $('span.plupload_add_text', target).html(_('Add Files')); } else { $('span.plupload_add_text', target).html(plupload.sprintf(_('%d files queued'), uploader.total.queued)); } $('a.plupload_start', target).toggleClass('plupload_disabled', uploader.files.length == (uploader.total.uploaded + uploader.total.failed)); // Scroll to end of file list fileList[0].scrollTop = fileList[0].scrollHeight; updateTotalProgress(); // Re-add drag message if there is no files if (!uploader.files.length && uploader.features.dragdrop && uploader.settings.dragdrop) { $('#' + id + '_filelist').append('
  • ' + _("Drag files here.") + '
  • '); } } function destroy() { delete uploaders[id]; uploader.destroy(); target.html(contents_bak); uploader = target = contents_bak = null; } uploader.bind("UploadFile", function(up, file) { $('#' + file.id).addClass('plupload_current_file'); }); uploader.bind('Init', function(up, res) { // Enable rename support if (!settings.unique_names && settings.rename) { target.on('click', '#' + id + '_filelist div.plupload_file_name span', function(e) { var targetSpan = $(e.target), file, parts, name, ext = ""; var fileContainer = targetSpan.closest('li'); if (!fileContainer.hasClass('plupload_delete')) { return; } // Get file name and split out name and extension file = up.getFile(targetSpan.parents('li')[0].id); name = file.name; parts = /^(.+)(\.[^.]+)$/.exec(name); if (parts) { name = parts[1]; ext = parts[2]; } // Display input element targetSpan.hide().after(''); targetSpan.next().val(name).focus().blur(function() { targetSpan.show().next().remove(); }).keydown(function(e) { var targetInput = $(this); if (e.keyCode == 13) { e.preventDefault(); // Rename file and glue extension back on file.name = targetInput.val() + ext; targetSpan.html(file.name); targetInput.blur(); } }); }); } $('#' + id + '_container').attr('title', 'Using runtime: ' + res.runtime); $('a.plupload_start', target).click(function(e) { if (!$(this).hasClass('plupload_disabled')) { uploader.start(); } e.preventDefault(); }); $('a.plupload_stop', target).click(function(e) { e.preventDefault(); uploader.stop(); }); $('a.plupload_start', target).addClass('plupload_disabled'); }); uploader.bind("Error", function(up, err) { var file = err.file, message; if (file) { message = err.message; if (err.details) { message += " (" + err.details + ")"; } if (err.code == plupload.FILE_SIZE_ERROR) { alert(_("Error: File too large:") + " " + file.name); } if (err.code == plupload.FILE_EXTENSION_ERROR) { alert(_("Error: Invalid file extension:") + " " + file.name); } file.hint = message; $('#' + file.id).attr('class', 'plupload_failed').find('a').css('display', 'block').attr('title', message); } if (err.code === plupload.INIT_ERROR) { setTimeout(function() { destroy(); }, 1); } }); uploader.bind("PostInit", function(up) { // features are populated only after input components are fully instantiated if (up.settings.dragdrop && up.features.dragdrop) { $('#' + id + '_filelist').append('
  • ' + _("Drag files here.") + '
  • '); } }); uploader.init(); uploader.bind('StateChanged', function() { if (uploader.state === plupload.STARTED) { $('li.plupload_delete a,div.plupload_buttons', target).hide(); uploader.disableBrowse(true); $('span.plupload_upload_status,div.plupload_progress,a.plupload_stop', target).css('display', 'block'); $('span.plupload_upload_status', target).html('Uploaded ' + uploader.total.uploaded + '/' + uploader.files.length + ' files'); if (settings.multiple_queues) { $('span.plupload_total_status,span.plupload_total_file_size', target).show(); } } else { updateList(); $('a.plupload_stop,div.plupload_progress', target).hide(); $('a.plupload_delete', target).css('display', 'block'); if (settings.multiple_queues && uploader.total.uploaded + uploader.total.failed == uploader.files.length) { $(".plupload_buttons,.plupload_upload_status", target).css("display", "inline"); uploader.disableBrowse(false); $(".plupload_start", target).addClass("plupload_disabled"); $('span.plupload_total_status,span.plupload_total_file_size', target).hide(); } } }); uploader.bind('FilesAdded', updateList); uploader.bind('FilesRemoved', function() { // since the whole file list is redrawn for every change in the queue // we need to scroll back to the file removal point to avoid annoying // scrolling to the bottom bug (see #926) var scrollTop = $('#' + id + '_filelist').scrollTop(); updateList(); $('#' + id + '_filelist').scrollTop(scrollTop); }); uploader.bind('FileUploaded', function(up, file) { handleStatus(file); }); uploader.bind("UploadProgress", function(up, file) { // Set file specific progress $('#' + file.id + ' div.plupload_file_status', target).html(file.percent + '%'); handleStatus(file); updateTotalProgress(); }); // Call setup function if (settings.setup) { settings.setup(uploader); } }); return this; } else { // Get uploader instance for specified element return uploaders[$(this[0]).attr('id')]; } }; })(jQuery, plupload);