UNPKG

zui

Version:

一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。

904 lines (832 loc) 50.7 kB
/* ======================================================================== * ZUI: uploader.js * http://zui.sexy * ======================================================================== * Copyright (c) 2014-2016 cnezsoft.com; Licensed MIT * ======================================================================== */ (function($, window, Plupload, Moxie, undefined) { 'use strict'; if(!$.zui.strCode) { $.zui.strCode = function(str) { var code = 0; if(str && str.length) { for(var i = 0; i < str.length; ++i) { code += i * str.charCodeAt(i); } } return code; }; } var notSupportDnd = ($.zui.browser && $.zui.browser.ie && $.zui.browser.ie < 11); var NAME = 'zui.uploader', // modal name FILE_TEMPLATE = '<div class="file"><div class="file-progress-bar"></div><div class="file-wrapper"><div class="file-icon"><i class="icon icon-file-o"></i></div><div class="content"><div class="file-name"></div><div class="file-size small text-muted">0KB</div></div><div class="actions"><div class="file-status" data-toggle="tooltip"><i class="icon"></i> <span class="text"></span></div><a data-toggle="tooltip" class="btn btn-link btn-download-file" target="_blank"><i class="icon icon-download-alt"></i></a><button type="button" data-toggle="tooltip" class="btn btn-link btn-reset-file" title="Repeat"><i class="icon icon-repeat"></i></button><button type="button" data-toggle="tooltip" class="btn btn-link btn-rename-file" title="Rename"><i class="icon icon-pencil"></i></button><button type="button" data-toggle="tooltip" title="Remove" class="btn btn-link btn-delete-file"><i class="icon icon-trash text-danger"></i></button></div></div></div>', DEFAULTS = { // qiniu: { // uptoken_url, // uptoken, // save_key, // domain, // get_new_uptoken, // key // }, // fileList: '', // 'default', 'large', 'grid', '>.file-list', '#myFileList', '<div class="uploader-files file-list"></div>' // fileTemplate: '', // fileFormater: null, // fileIconCreator: null, // staticFiles: null, rename: true, // renameExtension: false, renameByClick: true, // autoUpload: false, // browseByClickList: false, dropPlaceholder: true, // messageCreator: null, // NOT SUPPORT previewImageIcon: true, sendFileName: true, sendFileId: true, responseHandler: true, // limitFilesCount: false, // deleteConfirm: false, // removeUploaded: false, // statusCreator: null, // Function // previewImageSize: {width: 200, height: 200}, uploadedMessage: true, // deleteActionOnDone: false, // false, true or function // renameActionOnDone: false, // false, true or function, // autoResetFails: false, // if true auto reupload failed files // plupload options drop_element: 'self', // 'self', 'fileList', String or jQuery object, browse_button: 'hidden', // String or jQuery object // url: '', // String filters: {prevent_duplicates: true}, // {mime_types, max_file_size, prevent_duplicates} // headers: null, // Object // multipart: true, // true, false // multipart_params: null, // Object chunk_size: '1mb', // Number, String max_retries: 3, // resize: {}, // {width, height, crop, quality, preserve_headers}, // multi_selection: true, // true, false, // required_features: null, // String // unique_names: false, // true, false // runtimes: 'html5,flash,silverlight,html4', // String // file_data_name: 'file', // String flash_swf_url: 'lib/uploader/Moxie.swf', // String silverlight_xap_url: 'lib/uploader/Moxie.xap' // String }; var STATUS = { QUEUED : Plupload.QUEUED, UPLOADING : Plupload.UPLOADING, FAILED : Plupload.FAILED, DONE : Plupload.DONE, STOPPED : Plupload.STOPPED, STARTED : Plupload.STARTED }; STATUS[Plupload.QUEUED] = 'queue'; STATUS[Plupload.UPLOADING] = 'uploading'; STATUS[Plupload.FAILED] = 'failed'; STATUS[Plupload.DONE] = 'done'; var ERRORS = { GENERIC_ERROR : Plupload.GENERIC_ERROR, HTTP_ERROR : Plupload.HTTP_ERROR, IO_ERROR : Plupload.IO_ERROR, SECURITY_ERROR : Plupload.SECURITY_ERROR, INIT_ERROR : Plupload.INIT_ERROR, FILE_SIZE_ERROR : Plupload.FILE_SIZE_ERROR, FILE_EXTENSION_ERROR : Plupload.FILE_EXTENSION_ERROR, FILE_DUPLICATE_ERROR : Plupload.FILE_DUPLICATE_ERROR, IMAGE_FORMAT_ERROR : Plupload.IMAGE_FORMAT_ERROR, IMAGE_MEMORY_ERROR : Plupload.IMAGE_MEMORY_ERROR, IMAGE_DIMENSIONS_ERROR: Plupload.IMAGE_DIMENSIONS_ERROR }; // The uploader modal class var Uploader = function(element, options) { var that = this; that.name = NAME; that.$ = $(element).addClass('uploader'); options = that.getOptions(options); // Init lang var lang = $.isPlainObject(options.lang) ? ($.extend(true, {}, Uploader.LANG[lang.lang || $.zui.clientLang()], options.lang)) : Uploader.LANG[options.lang]; that.lang = lang; // Init file list element var $this = that.$; var fileList = options.fileList; var $list; if(!fileList || fileList == 'large' || fileList == 'grid') { $list = $this.find('.file-list,.uploader-files'); } else if(fileList.indexOf('>') === 0) $list = $this.find(fileList.substr(1)); else $list = $(fileList); if(!$list || !$list.length) $list = $('<div class="uploader-files file-list"></div>'); if(!$list.parent().length) $this.append($list); if(fileList == 'large') $list.addClass('file-list-lg'); else if(fileList == 'grid') $list.addClass('file-list-grid'); $list.children('.file').addClass('file-static'); that.$list = $list; if(options.browseByClickList || $list.hasClass('uploader-btn-browse')) { $list.addClass('uploader-btn-browse').on('click', '.file-wrapper > .actions,.file-renaming .file-name', function(e) { e.stopPropagation(); }); } // Init file template var template = options.fileTemplate; if(!template) { var $template = $list.find('.template'); if($template.length) { template = $template.first().clone().removeClass('template'); $template.remove(); } if(!template) template = FILE_TEMPLATE; } if(typeof template === 'string') { template = $(template); if(template.parent()) template = template.clone().removeClass('template'); } that.template = template; // Init browse button element var browseBtn = options.browse_button; var $btn = null; if(browseBtn) { if(browseBtn.indexOf('>') === 0) $btn = $this.find(browseBtn.substr(1)); else if(browseBtn !== 'hidden') $btn = $(browseBtn); } if(!$btn || !$btn.length) { $btn = $('<div class="uploader-btn-browse uploader-btn-hidden"></div>').appendTo($this); } that.$button = $btn.first(); // Init drop element var dropElement = options.drop_element; var $dropElement = (dropElement == 'fileList' ? that.$list : (dropElement == 'self' ? that.$ : $(dropElement))).first().addClass('file-drag-area'); if (!notSupportDnd) { var dropPlaceholder = options.dropPlaceholder; if(dropPlaceholder === true) dropPlaceholder = lang.dropPlaceholder; if(dropPlaceholder) $dropElement.attr('data-drop-placeholder', dropPlaceholder); } else { $dropElement.attr('data-drop-placeholder', ''); } that.$dropElement = $dropElement; // Init message that.$message = $this.find('.uploader-message').on('click', '.close', function() { that.hideMessage(); }); that.$status = $this.find('.uploader-status'); // Init actions $this.toggleClass('uploader-rename', !!options.rename); // Init plupload that.initPlupload(); // Bind events $this.on('click.' + NAME, '.uploader-btn-start', function(e) { that.start(); }).on('click.' + NAME, '.uploader-btn-browse', function(e) { if($(this).is(that.$button)) return; that.$button.trigger('click'); }).on('click.' + NAME, '.uploader-btn-stop', function(e) { that.stop(); }); $('body').on('dragleave.' + NAME + ' drop.' + NAME, function(e) { $this.removeClass('file-dragable'); // Below two lines for firefox, open a new tab after drop file e.preventDefault(); e.stopPropagation(); }).on('dragover.' + NAME + ' dragenter.' + NAME, function(e) { $this.addClass('file-dragable'); }); $dropElement.on('dragleave.' + NAME + ' drop.' + NAME, function(e) { $this.removeClass('file-drag-enter'); }).on('dragover.' + NAME + ' dragenter.' + NAME, function(e) { $this.addClass('file-drag-enter'); }); $list.on('click.' + NAME, '.btn-delete-file', function() { var $file = $(this).closest('.file'); var file = $file.data('file'); var deleteActionOnDoneOption = options.deleteActionOnDone; var doneActionAble = file.status === Plupload.DONE && $.isFunction(deleteActionOnDoneOption); if(file.status === Plupload.QUEUED || file.status === Plupload.FAILED || doneActionAble) { var doRemoveFile = function() { that.removeFile(file); }; var removeFile = function() { if(doneActionAble) { var result = deleteActionOnDoneOption.call(that, file, doRemoveFile); if(result === true) { doRemoveFile(); } } else { doRemoveFile(); } }; var deleteConfirmOption = options.deleteConfirm; if(deleteConfirmOption) { var confirmMessage = $.isFunction(deleteConfirmOption) ? deleteConfirmOption(file) : (deleteConfirmOption === true ? lang.deleteConfirm : deleteConfirmOption); confirmMessage = confirmMessage.format(file); if(window.bootbox) { window.bootbox.confirm(confirmMessage, function(result) { if(result) removeFile(); }); } else { if(window.confirm(confirmMessage)) removeFile(); } } else { removeFile(); } } }).on('click.' + NAME, '.btn-reset-file', function() { var $file = $(this).closest('.file'); var file = that.plupload.getFile($file.data('id')) || $file.data('file'); if(file.status === Plupload.FAILED) { file.status = Plupload.QUEUED; that.showFile(file); if(options.autoUpload) that.start(); } }); if(options.rename) { $list.toggleClass('file-rename-by-click', !!options.renameByClick) .toggleClass('file-show-rename-action-on-done', !!options.renameActionOnDone); $list.on('click.' + NAME, '.btn-rename-file' + (options.renameByClick ? ',.file-name' : ''), function() { var $file = $(this).closest('.file'); if($file.hasClass('file-renaming')) return; var file = that.plupload.getFile($file.data('id')) || $file.data('file'); var renameActionOnDoneOption = options.renameActionOnDone; var renameActionAble = file.status === Plupload.DONE && $.isFunction(renameActionOnDoneOption); if(renameActionAble || file.status === Plupload.QUEUED) { var $filename = $file.find('.file-name').first(); $file.addClass('file-renaming'); that.showFile(file); if(!options.renameExtension && file.ext) { $filename.text(file.name.substr(0, file.name.length - file.ext.length - 1)); } $filename.attr('contenteditable', 'true').one('blur', function() { var filename = $.trim($filename.text()); var renameFile = function() { if(filename !== undefined && filename !== null && filename !== '') { var ext = file.ext; if(ext.length && !options.renameExtension && filename.lastIndexOf('.' + ext) !== (filename.length - ext.length - 1)) { filename += '.' + ext; } file.name = filename; } that.showFile(file); }; if(renameActionAble) { var result = renameActionOnDoneOption.call(that, file, filename, renameFile); if(result === true) { renameFile(); } else if(result === false) { that.showFile(file); } } else { renameFile(); } $file.removeClass('file-renaming'); $filename.off('keydown.' + NAME).attr('contenteditable', null); }).on('keydown.' + NAME, function(e) { if(e.keyCode === 13) { $filename.blur(); e.preventDefault(); } }).focus(); } }); } $list.toggleClass('file-show-delete-action-on-done', !!options.deleteActionOnDone); // Init static files that.staticFilesSize = 0; that.staticFilesCount = 0; if(options.staticFiles) { $.each(options.staticFiles, function(idx, file) { file = $.extend({status: Plupload.DONE}, file); file.static = true; if(!file.id) file.id = $.zui.uuid(); that.showFile(file); if (file.size) { that.staticFilesSize += file.size; that.staticFilesCount++; } }); } that.callEvent('onInit'); }; // default options Uploader.DEFAULTS = DEFAULTS; Uploader.prototype.showMessage = function(message, type, time) { var that = this; var $msg = that.$message; if(!message) that.hideMessage(); else clearTimeout(that.lastDismissMessage); type = type || 'danger'; if(time === undefined) time = type === 'danger' ? 10 : 6; if(time < 20) time *= 1000; var $content = $msg.find('.content'); if($content.length) $content.empty().append(message); else $msg.empty().append(message); $msg.attr('data-type', type).slideDown('fast'); if(time) { that.lastDismissMessage = setTimeout(function() { that.hideMessage(); }, time); } }; Uploader.prototype.hideMessage = function() { clearTimeout(this.lastDismissMessage); this.$message.slideUp('fast'); }; Uploader.prototype.start = function() { if (this.options.autoResetFails) { $.each(this.getFiles(), function(index, file) { if(file.status === Plupload.FAILED) { file.status = Plupload.QUEUED; } }); } return this.plupload.start(); }; Uploader.prototype.stop = function() { return this.plupload.stop(); }; Uploader.prototype.getState = function() { return this.plupload.state; }; Uploader.prototype.isStarted = function() { return this.getState() === Plupload.STARTED; }; Uploader.prototype.isStopped = function() { return this.getState() === Plupload.STOPPED; }; Uploader.prototype.getFiles = function() { return this.plupload.files; }; Uploader.prototype.getTotal = function() { return this.plupload.total; }; Uploader.prototype.disableBrowse = function(disable) { this.$.find('.uploader-btn-browse').attr('disable', disable ? 'disable' : null).toggle('disable', !!disable); return this.plupload.disableBrowse(); }; Uploader.prototype.getFile = function(id) { return this.plupload.getFile(id); }; Uploader.prototype.destroy = function() { var that = this; var eventNamespace = '.' + NAME; that.$.off(eventNamespace).data(NAME, null); that.$list.off(eventNamespace); that.$dropElement.off(eventNamespace); $('body').off(eventNamespace); that.plupload.destroy(); }; // see https://github.com/moxiecode/moxie/wiki/API Uploader.prototype.previewImageSrc = function(file, callback) { if(!file || !file.getSource || !/image\//.test(file.type)) return; var size = $.extend({width: 200, height: 200}, this.options.previewImageSize); if(file.type == 'image/gif') { //mOxie.Image only support jpg and png var fr = new Moxie.file.FileReader(); fr.onload = function() { callback(fr.result); fr.destroy(); fr = null; }; fr.readAsDataURL(file.getSource()); } else { var preloader = new Moxie.image.Image(); preloader.onload = function() { // compressImage preloader.downsize(size.width, size.height); var imgsrc = preloader.type == 'image/jpeg' ? preloader.getAsDataURL('image/jpeg', 80) : preloader.getAsDataURL(); // return base64 data callback(imgsrc); preloader.destroy(); preloader = null; }; preloader.load(file.getSource()); } }; Uploader.prototype.createFileIcon = function(file) { var fileType = file.type; var ext = file.ext; var icon = 'file-o'; var types = fileType ? fileType.split('/') : null; var type = (types && types.length) ? types[0] : '', subType = (types && types.length) > 1 ? types[1] : ''; if(type == 'image') icon = 'file-image'; else if(ext == 'doc' || ext == 'docx' || ext == 'pages') icon = 'file-word'; else if(ext == 'ppt' || ext == 'pptx' || ext == 'key') icon = 'file-powerpoint'; else if(ext == 'xls' || ext == 'xlsx' || ext == 'numbers') icon = 'file-excel'; else if(ext == 'html' || ext == 'htm') icon = 'globe'; else if(ext == 'js' || ext == 'php' || ext == 'cs' || ext == 'jsx' || ext == 'css' || ext == 'less' || ext == 'json' || ext == 'java' || ext == 'lua' || ext == 'py' || ext == 'c' || ext == 'cpp' || ext == 'swift' || ext == 'h' || ext == 'sh' || ext == 'rb' || ext == 'yml' || ext == 'ini' || ext == 'sql' || ext == 'xml') icon = 'file-code'; else if(ext == 'apk') icon = 'android'; else if(ext == 'exe') icon = 'windows'; else if(ext == 'pkg' || ext == 'msi' || ext == 'dmg') icon = 'cube'; else if(ext == 'epub') icon = 'book'; else if(ext == 'sketch') icon = 'diamond'; else if(subType == 'zip' || subType == 'x-rar' || subType == 'x-7z-compressed') icon = 'file-archive'; else if(subType == 'pdf') icon = 'file-pdf'; else if(type == 'video') icon = 'file-movie'; else if(type == 'audio') icon = 'file-audio'; else if(type == 'text') icon = 'file-text-o'; return '<i class="icon icon-' + icon + ' file-icon-' + ext + '" data-type="' + fileType + '"' + (ext ? ' data-ext="' + ext + '"' : '') + '></i>'; }; Uploader.prototype.getFileItem = function(file) { var that = this; if(typeof file == 'string') { file = that.plupload.getFile(file); } if(!file) return null; var filename = file.name; if(filename && file.ext === undefined) { var ext = filename.lastIndexOf('.'); if(ext > -1) ext = filename.substr(ext + 1); else ext = ''; file.ext = ext; if(file.type && /image\//.test(file.type)) { file.isImage = file.ext; } } var $file = $('#file-' + file.id); if(!$file.length) { if($.isFunction(that.template)) { $file = $(that.template(file, that)); } else { $file = $(that.template).clone(); $file.find('.btn-rename-file').attr('title', that.lang.rename); $file.find('.btn-delete-file').attr('title', that.lang.remove); $file.find('.btn-reset-file').attr('title', that.lang.repeat); $file.find('.btn-download-file').attr('title', that.lang.download).attr('download', file.name); } $file.data('id', file.id) .toggleClass('file-static', !!file.static) .attr('id', 'file-' + file.id) .appendTo(that.$list); if($.fn.tooltip) $file.find('[data-toggle="tooltip"]').tooltip(); } return $file; }; Uploader.prototype.showFile = function(file, responseObject) { var that = this; if($.isArray(file)) { $.each(file, function(idx, f) { that.showFile(f, responseObject); }); return; } if(typeof file == 'string') { file = that.plupload.getFile(file); } if(!file) return; var $file = that.getFileItem(file); if(!$file || !$file.length) { return; } var options = that.options; var status = STATUS[file.status]; if(options.fileFormater) { options.fileFormater.call(that, $file, file, status); } else { var downloadUrl = (status == 'done' && file.url) ? file.url : null; $file.find('.file-name').text(file.name); $file.find('.file-size').text((status == 'uploading' ? (Plupload.formatSize(Math.floor(file.size*file.percent/100)).toUpperCase() + '/') : '') + Plupload.formatSize(file.size).toUpperCase()); $file.find('.file-icon').html(options.fileIconCreator ? options.fileIconCreator(file.type, file, that) : that.createFileIcon(file)).css('color', 'hsl(' + $.zui.strCode(file.type || file.ext) + ', 70%, 40%)'); $file.find('.file-progress-bar').css('width', file.percent + '%'); var $status = $file.find('.file-status').attr('title', that.lang[status]); $status.find('.text').text(status == 'uploading' ? (file.percent + '%') : ((status == 'failed') ? that.lang[status] : '')); if($.fn.tooltip) $file.find('[data-toggle="tooltip"]').tooltip('fixTitle'); $file.find('a.btn-download-file, a.file-name').attr('href', downloadUrl); } if(options.previewImageIcon && file.isImage) { var setPreviewImage = function() { $file.find('.file-icon').html('<div class="file-icon-image" style="background-image: url(' + file.previewImage + ')"></div>'); }; if(file.previewImage) { setPreviewImage(); } else { that.previewImageSrc(file, function(src) { file.previewImage = src; setPreviewImage(); }); } } $file.attr('data-status', status).data('file', file); }; Uploader.prototype.showStatus = function() { var that = this; var plupload = that.plupload; var $status = that.$status; var state = plupload.state, total = plupload.total, statusText = '', totalCount = plupload.files.length; if(that.options.statusCreator) { statusText = that.options.statusCreator(total, state, that); } else { var stateObj = { uploading: Math.max(0, Math.min(totalCount, total.uploaded + 1)), total: that.staticFilesCount + totalCount, size: Plupload.formatSize(total.size + that.staticFilesSize).toUpperCase(), queue: total.queued, failed: total.failed, uploaded: total.uploaded, uploadedSize: Plupload.formatSize(total.loaded).toUpperCase(), percent: total.percent, speed: Plupload.formatSize(total.bytesPerSec).toUpperCase() + '/S' }; if(state == Plupload.STARTED) { statusText = that.lang.startedStatusText.format(stateObj); } else { if(totalCount < 1) { statusText = that.lang.initStatusText; } else { statusText = that.lang.stoppedStatusText.format(stateObj); } } } $status.html(statusText); if(total.uploaded < 1) $status.find('.uploader-status-uploaded').remove(); if(total.failed < 1) $status.find('.uploader-status-failed').remove(); if(total.queued < 1) $status.find('.uploader-status-queue').remove(); if($.fn.tooltip) $status.find('[data-toggle="tooltip"]').tooltip(); }; Uploader.prototype.delayShowStatus = function(delay) { var that = this; if(that.delayStatusTask) return; that.delayStatusTask = true; if(delay === undefined) delay = 500; that.delayStatusTask = setTimeout(function() { that.showStatus(); that.delayStatusTask = false; }, delay); }; Uploader.prototype.removeFile = function(file, onlyRemoveElement) { var that = this; if(typeof file == 'string') { file = that.plupload.getFile(file); } if(onlyRemoveElement || file.static) { var $file = $('#file-' + file.id); if($.fn.tooltip) { $file.find('[data-toggle="tooltip"]').tooltip('destroy'); $('.tooltip').remove(); } $file.fadeOut(function() { $(this).remove(); }); } else { that.plupload.removeFile(file); } }; Uploader.prototype.initPlupload = function() { var that = this; var options = that.options; var plOptions = $.extend({}, options, { browse_button: that.$button[0], container: that.$[0], drop_element: that.$dropElement[0], multipart_params: null }); var eventHandlers = { FilesAdded: function(uploader, files) { var limitFilesCount = options.limitFilesCount; if(limitFilesCount) { if(limitFilesCount === true) limitFilesCount = 1; var existCount = that.$list.children('.file').length; if((existCount + files.length) > limitFilesCount) { that.showMessage(that.lang.limitFilesCountMessage.format({count: limitFilesCount}), 'warning'); var newFiles = []; for(var i = 0; i < files.length; ++i) { if((existCount + i + 1) <= limitFilesCount) { newFiles.push(files[i]); } else { uploader.removeFile(files[i]); } } if(!newFiles.length) return; files = newFiles; } } that.showFile(files); if(options.autoUpload) that.start(); that.showStatus(); that.callEvent('onFilesAdded', [files]); }, UploadProgress: function(uploader, file) { that.showFile(file); that.delayShowStatus(); that.callEvent('onUploadProgress', file); }, FileUploaded: function(uploader, file, responseObject) { if(responseObject) { var responseData = typeof responseObject === 'object' ? responseObject.response : responseObject; try {file.remoteData = $.parseJSON(responseData);} catch(e) {} } if(that.qiniuEnable && file.remoteData) { file.url = uploader.settings.domain + file.remoteData.key; } var responseHandlerOption = options.responseHandler; if(responseHandlerOption) { var error = null; if($.isFunction(responseHandlerOption)) { error = responseHandlerOption.call(that, responseObject, file); } else if(responseObject.response) { var json = file.remoteData; if($.isPlainObject(json)) { var result = json.status || json.result; result = result === 'ok' || result === 'success' || result === 200; if (result) { if(json.id !== undefined) file.remoteId = json.id; if(json.url !== undefined) file.url = json.url; if(json.name !== undefined) file.name = json.name; } else { error = {message: json.message, data: json}; } } } if(error) { error = $.isPlainObject(error) ? error : {message: error}; file.status = Plupload.FAILED; if(error.code === undefined) error.code = Plupload.GENERIC_ERROR; error.file = file; error.responseObject = responseObject; file.errorMessage = error.message; return; } } if(file.status === Plupload.DONE) { that.lastUploadedCount++; } that.showFile(file, responseObject); that.showStatus(); that.callEvent('onFileUploaded', [file, responseObject]); if(file.status === Plupload.DONE) { var optionRemoveUploaded = options.removeUploaded; if(optionRemoveUploaded) { setTimeout(function() { $('#file-' + file.id).fadeOut(function() { $(this).remove(); }); }, (typeof optionRemoveUploaded) === 'number' ? optionRemoveUploaded : 2000); } } }, UploadComplete: function(uploader, files) { that.showFile(files); that.showStatus(); var uploadedMessage = options.uploadedMessage; if(uploadedMessage) { var uploadedCount = that.lastUploadedCount; var failedCount = 0; var failMessages = []; $.each(files, function(idx, file) { if(file.status === Plupload.FAILED) { failedCount++; if (file.errorMessage) { failMessages.push(file.errorMessage); delete file.errorMessage; } } }); var msg = failMessages && failMessages.length ? ('<p>' + failMessages.join(',') + '</p>') : '', msgData = { uploaded: uploadedCount, failed: failedCount }; if(typeof uploadedMessage === 'string') { msg += uploadedMessage.format(msgData); } else if($.isFunction(uploadedMessage)) { msg += uploadedMessage(msgData); } else { msg += that.lang[failedCount > 0 ? 'uploadHasFailedMessage' : (uploadedCount > 0 ? 'uploadSuccessMessage' : 'uploadEmptyMessage')].format(msgData); } that.showMessage(msg, failedCount > 0 ? 'danger' : (uploadedCount > 0 ? 'success' : 'warning'), 3); } that.callEvent('onUploadComplete', [files]); }, FilesRemoved: function(uploader, files) { $.each(files, function(idx, file) { that.removeFile(file, true); }); that.showStatus(); that.callEvent('onFilesRemoved', files); }, ChunkUploaded: function(uploader, file, responseObject) { that.callEvent('onChunkUploaded', [file, responseObject]); }, UploadFile: function(uploader, file) { that.showStatus(); that.callEvent('onUploadFile', file); }, BeforeUpload: function(uploader, file) { var oldParams = uploader.getOption('multipart_params'); var multipartParamsOption = options.multipart_params; var params = {}; if (oldParams && oldParams.key) { params.key = oldParams.key; } if (oldParams && oldParams.token) { params.token = oldParams.token; } if(options.sendFileName) params[options.sendFileName === true ? 'name' : options.sendFileName] = file.name; if(options.sendFileId) params[options.sendFileId === true ? 'uuid' : options.sendFileId] = file.id; params = $.extend(params, $.isFunction(multipartParamsOption) ? multipartParamsOption(file, params) : multipartParamsOption); uploader.setOption('multipart_params', params); that.callEvent('onBeforeUpload', file); }, Refresh: function(uploader) { that.showStatus(); that.callEvent('onRefresh'); }, StateChanged: function(uploader) { if(uploader.state === Plupload.STARTED) { that.lastUploadedCount = 0; } that.$.toggleClass('uploader-started', Plupload.STARTED === uploader.state); that.hideMessage(); that.showStatus(); that.callEvent('onStateChanged', uploader.state); }, QueueChanged: function(uploader) { that.showStatus(); that.callEvent('onQueueChanged'); }, Error: function(uploader, error) { var type = 'danger'; if(error.code === Plupload.FILE_SIZE_ERROR || error.code === Plupload.FILE_SIZE_ERROR || error.code === Plupload.FILE_EXTENSION_ERROR || error.code === Plupload.FILE_DUPLICATE_ERROR || error.code === Plupload.MAGE_FORMAT_ERROR) type = 'warning'; that.showMessage(error.message, type); that.callEvent('onError', error); } }; Plupload.addI18n(that.lang.i18n); that.qiniuEnable = $.isPlainObject(options.qiniu) && window.Qiniu; if(that.qiniuEnable) { var qiniuOptions = options.qiniu; var qiniuKeyFunc = qiniuOptions.key; delete plOptions.qiniu; if(qiniuKeyFunc) { delete qiniuOptions.key; if($.isFunction(qiniuKeyFunc)) { eventHandlers.Key = qiniuKeyFunc; } } else { eventHandlers.Key = function(uploader, file) { return file.name; }; } qiniuOptions.init = eventHandlers; plOptions = $.extend(plOptions, qiniuOptions); var qiniuSKD = new QiniuJsSDK(); var plupload = qiniuSKD.uploader(plOptions); that.plupload = plupload; } else { var plupload = new Plupload.Uploader(plOptions); plupload.init(); that.plOptions = plOptions; that.plupload = plupload; $.each(eventHandlers, function(eventName, eventHandler) { plupload.bind(eventName, eventHandler); }); } }; // Get and init options Uploader.prototype.getOptions = function(options) { this.options = $.extend({ lang: $.zui.clientLang() }, DEFAULTS, this.$.data(), options); return this.options; }; // Call event helper Uploader.prototype.callEvent = function(name, params) { var that = this; if(!$.isArray(params)) params = [params]; that.$.trigger(name, params); if($.isFunction(that.options[name])) { return that.options[name].apply(that, params); } }; // Extense jquery element $.fn.uploader = function(option, params) { return this.each(function() { var $this = $(this); var data = $this.data(NAME); var options = typeof option == 'object' && option; if(!data) $this.data(NAME, (data = new Uploader(this, options))); if(typeof option == 'string') data[option](params); }); }; Uploader.NAME = NAME; Uploader.STATUS = STATUS; Uploader.ERRORS = ERRORS; Uploader.NAME = NAME; Uploader.LANG = { zh_cn: {"limitFilesCountMessage": "所有文件数目不能超过 {count} 个,如果要上传此文件请先从列表移除文件。", "uploadEmptyMessage": "没有文件等待上传。", "uploadSuccessMessage": "已上传 <strong>{uploaded}</strong> 个文件。", "uploadHasFailedMessage": "已上传 <strong>{uploaded}</strong> 个文件,<strong>{failed}</strong> 个文件上传失败。", "startedStatusText": "正在上传第 <strong>{uploading}</strong> 个文件,共 <strong title=\"总大小:{size}\" data-toggle=\"tooltip\" class=\"text-primary\">{total}</strong> 个文件,<span class=\"uploader-status-uploaded\">已上传 <strong title=\"总大小:{uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> 个文件,</span><span class=\"uploader-status-failed\"><strong>{failed}</strong> 个上传失败,</span>进度 <strong>{percent}%</strong>,平均速度 <strong>{speed}</strong>。", "initStatusText": "添加文件或拖放文件来上传。", "stoppedStatusText": "共 <strong title=\"总大小:{size}\" data-toggle=\"tooltip\" class=\"text-primary\">{total}</strong> 个文件<span class=\"uploader-status-queue\">,<strong>{queue}</strong> 个文件等待上传</span><span class=\"uploader-status-uploaded\">,已上传 <strong title=\"总大小:{uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> 个文件</span><span class=\"uploader-status-failed\">,<strong>{failed}</strong> 个上传失败</span><span class=\"uploader-status-uploaded\">,平均速度 <strong>{speed}</strong></span>。", "deleteConfirm": "确定移除文件【{name}】?", "download": "下载", "rename": "重命名", "repeat": "重新上传", "remove": "移除", "dropPlaceholder": "将文件拖放至在此处。", "queue": "待上传", "uploading": "正在上传", "failed": "失败", "done": "已上传", "i18n": {"Stop Upload":"停止上传","Upload URL might be wrong or doesn't exist.":"上传的URL可能是错误的或不存在。","tb":"tb","Size":"大小","Close":"关闭","You must specify either browse_button or drop_element.":"您必须指定 browse_button 或者 drop_element。","Init error.":"初始化错误。","Add files to the upload queue and click the start button.":"将文件添加到上传队列,然后点击”开始上传“按钮。","List":"列表","Filename":"文件名","%s specified, but cannot be found.":"%s 已指定,但是没有找到。","Image format either wrong or not supported.":"图片格式错误或者不支持。","Status":"状态","HTTP Error.":"HTTP 错误。","Start Upload":"开始上传","Error: File too large:":"错误: 文件太大:","kb":"kb","Duplicate file error.":"无法添加重复文件。","File size error.":"文件大小错误。","N/A":"N/A","gb":"gb","Error: Invalid file extension:":"错误:无效的文件扩展名:","Select files":"选择文件","%s already present in the queue.":"%s 已经在当前队列里。","Resoultion out of boundaries! <b>%s</b> runtime supports images only up to %wx%hpx.":"超限。<b>%s</b> 支持最大 %wx%hpx 的图片。","File: %s":"文件: %s","b":"b","Uploaded %d/%d files":"已上传 %d/%d 个文件","Upload element accepts only %d file(s) at a time. Extra files were stripped.":"每次只接受同时上传 %d 个文件,多余的文件将会被删除。","%d files queued":"%d 个文件加入到队列","File: %s, size: %d, max file size: %d":"文件: %s, 大小: %d, 最大文件大小: %d","Thumbnails":"缩略图","Drag files here.":"把文件拖到这里。","Runtime ran out of available memory.":"运行时已消耗所有可用内存。","File count error.":"文件数量错误。","File extension error.":"文件扩展名错误。","mb":"mb","Add Files":"增加文件"}}, zh_tw: {"limitFilesCountMessage": "所有文件數目不能超過 {count} 個。","uploadEmptyMessage": "没有文件等待上傳。", "uploadSuccessMessage": "已上傳 <strong>{uploaded}</strong> 个文件。", "uploadHasFailedMessage": "文件上傳完成,已上傳 <strong>{uploaded}</strong> 個文件,<strong>{failed}</strong> 個文件上傳失败。", "startedStatusText": "正在上傳第<strong>{uploading}</strong> 個文件,共<strong title=\"總大小:{size}\" data-toggle=\"tooltip\" class=\"text -primary\">{total}</strong> 個文件,<span class=\"uploader-status-uploaded\">已上傳<strong title=\"總大小:{uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> 個文件,</span><span class=\"uploader-status-failed\"><strong>{failed}</ strong> 個上傳失敗,</span>進度<strong>{percent}%</strong>,平均速度<strong>{speed}</strong>。", "initStatusText": "添加文件或拖放文件來上傳。", "stoppedStatusText": "共<strong title=\"總大小:{size}\" data-toggle=\"tooltip\" class=\"text-primary\">{total}</strong> 個文件<span class=\"uploader-status-queue\">,<strong>{queue}</strong> 個文件等待上傳</span><span class=\"uploader-status-uploaded\">,已上傳<strong title=\"總大小:{uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> 個文件</span><span class=\" uploader-status-failed\">,<strong>{failed}</strong> 個上傳失敗</span><span class=\"uploader-status-uploaded\">,平均速度<strong>{speed}< /strong></span>。", "deleteConfirm": "確定移除文件【{name}】?", "download": "下载", "rename": "重命名", "repeat": "重新上傳", "remove": "移除", "dropPlaceholder": "將文件拖放至在此處。", "queue": "待上傳", "uploading": "正在上傳", "failed": "失敗", "done": "已上傳", "i18n": {"Stop Upload":"停止上傳","Upload URL might be wrong or doesn't exist.":"檔案URL可能有誤或者不存在。","tb":"tb","Size":"大小","Close":"關閉","You must specify either browse_button or drop_element.":"您必須指定 browse_button 或 drop_element。","Init error.":"初始化錯誤。","Add files to the upload queue and click the start button.":"將檔案加入上傳序列,然後點選”開始上傳“按鈕。","List":"清單","Filename":"檔案名稱","%s specified, but cannot be found.":"找不到已選擇的 %s。","Image format either wrong or not supported.":"圖片格式錯誤或者不支援。","Status":"狀態","HTTP Error.":"HTTP 錯誤。","Start Upload":"開始上傳","Error: File too large:":"錯誤: 檔案大小太大:","kb":"kb","Duplicate file error.":"錯誤:檔案重複。","File size error.":"錯誤:檔案大小超過限制。","N/A":"N/A","gb":"gb","Error: Invalid file extension:":"錯誤:不接受的檔案格式:","Select files":"選擇檔案","%s already present in the queue.":"%s 已經存在目前的檔案序列。","Resoultion out of boundaries! <b>%s</b> runtime supports images only up to %wx%hpx.":"圖片解析度超出範圍! <b>%s</b> 最高只支援到 %wx%hpx。","File: %s":"檔案: %s","b":"b","Uploaded %d/%d files":"已上傳 %d/%d 個文件","Upload element accepts only %d file(s) at a time. Extra files were stripped.":"每次只能上傳 %d 個檔案,超過限制數量的檔案將被忽略。","%d files queued":"%d 個檔案加入到序列","File: %s, size: %d, max file size: %d":"檔案: %s, 大小: %d, 檔案大小上限: %d","Thumbnails":"縮圖","Drag files here.":"把檔案拖曳到這裡。","Runtime ran out of available memory.":"執行時耗盡了所有可用的記憶體。","File count error.":"檔案數量錯誤。","File extension error.":"檔案副檔名錯誤。","mb":"mb","Add Files":"增加檔案"}}, en: {"limitFilesCountMessage": "All files count can not over {count}.","uploadEmptyMessage": "No file in queue to upload", "uploadSuccessMessage": "Uploaded <strong>{uploaded}</strong> files。", "uploadHasFailedMessage": "Uploaded complete, <strong>{uploaded}</strong> success, <strong>{failed}</strong> failed.", "startedStatusText": "Uploading NO.<strong>{uploading}</strong> file, total <strong title=\"Total size: {size}\" data-toggle=\"tooltip\" class=\"text-primary\">{total}</strong> files, <span class=\"uploader-status-uploaded\">Uploaded <strong title=\"Total size: {uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> files, </span><span class=\"uploader-status-failed\"><strong>{failed}</strong> failed, </span>progress <strong>{percent}%</strong>, average spped <strong>{speed}</strong>。", "initStatusText": "Append or drag file here.", "stoppedStatusText": "Total <strong title=\"Total size: {size}\" data-toggle=\"tooltip\" class=\"text-primary\">{total}</strong> files<span class=\"uploader-status-queue\">, <strong>{queue}</strong> files in queue</span><span class=\"uploader-status-uploaded\">, uploaded <strong title=\"Total size: {uploadedSize}\" data-toggle=\"tooltip\" class=\"text-primary\">{uploaded}</strong> files</span><span class=\"uploader-status-failed\">, <strong>{failed}</strong> failed</span><span class=\"uploader-status-uploaded\">, average spped <strong>{speed}</strong></span>。", "deleteConfirm": "Remove file \"{name}\" form upload queue?", "rename": "Rename", "download": "Download", "repeat": "Repeat", "remove": "Remove", "dropPlaceholder": "Drop file here.", "queue": "Wait", "uploading": "Uploading", "failed": "Failed", "done": "Done", "i18n": {"Stop Upload":"Stop Upload","Upload URL might be wrong or doesn't exist.":"Upload URL might be wrong or doesn't exist.","tb":"tb","Size":"Size","Close":"Close","You must specify either browse_button or drop_element.":"You must specify either browse_button or drop_element.","Init error.":"Init error.","Add files to the upload queue and click the start button.":"Add files to the upload queue and click the start button.","List":"List","Filename":"Filename","%s specified, but cannot be found.":"%s specified, but cannot be found.","Image format either wrong or not supported.":"Image format either wrong or not supported.","Status":"Status","HTTP Error.":"HTTP Error.","Start Upload":"Start Upload","Error: File too large:":"Error: File too large:","kb":"kb","Duplicate file error.":"Duplicate file error.","File size error.":"File size error.","N/A":"N/A","gb":"gb","Error: Invalid file extension:":"Error: Invalid file extension:","Select files":"Select files","%s already present in the queue.":"%s already present in the queue.","Resoultion out of boundaries! <b>%s</b> runtime supports images only up to %wx%hpx.":"Resoultion out of boundaries! <b>%s</b> runtime supports images only up to %wx%hpx.","File: %s":"File: %s","b":"b","Uploaded %d/%d files":"Uploaded %d/%d files","Upload element accepts only %d file(s) at a time. Extra files were stripped.":"Upload element accepts only %d file(s) at a time. Extra files were stripped.","%d files queued":"%d files queued","File: %s, size: %d, max file size: %d":"File: %s, size: %d, max file size: %d","Thumbnails":"Thumbnails","Drag files here.":"Drag files here.","Runtime ran out of available memory.":"Runtime ran out of available memory.","File count error.":"File count error.","File extension error.":"File extension error.","mb":"mb","Add Files":"Add Files"}} }; $.zui.plupload = Plupload; $.zui.moxie = Moxie; $.zui.Uploader = Uploader; $.fn.uploader.Constructor = Uploader; // For qiniu if(!window.mOxie) window.mOxie = { Env: Moxie.core.utils.Env, XMLHttpRequest: Moxie.xhr.XMLHttpRequest }; // Auto call uploader after document load complete $(function() { $('[data-ride="uploader"]').uploader(); }); }(jQuery, window, plupload, moxie, undefined));