dmuploader.js 9.57 KB
/*
 * dmuploader.js - Jquery File Uploader - 0.1
 * http://www.daniel.com.uy/projects/jquery-file-uploader/
 * 
 * Copyright (c) 2013 Daniel Morales
 * Dual licensed under the MIT and GPL licenses.
 * http://www.daniel.com.uy/doc/license/
 */

(function($) {
  var pluginName = 'dmUploader';

  // These are the plugin defaults values
  var defaults = {
    url: document.URL,
    method: 'POST',
    extraData: {},
    maxFileSize: 0,
    maxFiles: 0,
    allowedTypes: '*',
    extFilter: null,
    dataType: null,
    fileName: 'Filedata',
    onInit: function(){},
    onFallbackMode: function() {message},
    onNewFile: function(id, file){},
    onBeforeUpload: function(id){},
    onComplete: function(){},
    onUploadProgress: function(id, percent){},
    onUploadSuccess: function(id, data){},
    onUploadError: function(id, message){},
    onUploadAbort: function(id, data){},
    onFileTypeError: function(file){},
    onFileSizeError: function(file){},
    onFileExtError: function(file){},
    onFilesMaxError: function(file){}
  };

  var DmUploader = function(element, options)
  {
    this.element = $(element);

    this.settings = $.extend({}, defaults, options);

    if(!this.checkBrowser()){
      return false;
    }

    this.init();

    return true;
  };

  DmUploader.prototype.checkBrowser = function()
  {
    if(window.FormData === undefined){
      this.settings.onFallbackMode.call(this.element, 'Browser doesn\'t support Form API');

      return false;
    }

    if(this.element.find('input[type=file]').length > 0){
      return true;
    }

    if (!this.checkEvent('drop', this.element) || !this.checkEvent('dragstart', this.element)){
      this.settings.onFallbackMode.call(this.element, 'Browser doesn\'t support Ajax Drag and Drop');

      return false;
    }

    return true;
  };

  DmUploader.prototype.checkEvent = function(eventName, element)
  {
    var element = element || document.createElement('div');
    var eventName = 'on' + eventName;

    var isSupported = eventName in element;

    if(!isSupported){
      if(!element.setAttribute){
        element = document.createElement('div');
      }
      if(element.setAttribute && element.removeAttribute){
        element.setAttribute(eventName, '');
        isSupported = typeof element[eventName] == 'function';

        if(typeof element[eventName] != 'undefined'){
          element[eventName] = undefined;
        }
        element.removeAttribute(eventName);
      }
    }

    element = null;
    return isSupported;
  };

  DmUploader.prototype.init = function()
  {
    var widget = this;

    widget.queue = new Array();
    widget.queuePos = -1;
    widget.queueRunning = false;
    //Ԫitem mark
    widget.queueMark = new Array();
   	// xhrobjs
    widget.queueXhr = new Array();
	
    var isDropEventOff = widget.settings.dropEventOff;
    if(!!!isDropEventOff){
    	// -- Drag and drop event
	    widget.element.on('drop', function (evt){
	      evt.preventDefault();
	      var files = evt.originalEvent.dataTransfer.files;
		  //-- Clear file queue
	      widget.reset();
	      //-- Give User Confirm Dialog
		  widget.settings.onDrop.call(widget.element, files, function(_files) {
			if(_files) {
				widget.queueFiles(_files);
			}
		  });
	    });
    }

    //-- Optional File input to make a clickable area
    widget.element.find('input[type=file]').on('change', function(evt){
	  var self = this;    	
      var files = evt.target.files;
      // 修复超过10个附件不能再发附件必须要重新刷新的问题, 这会影响系统广播的附件,因为系统广播的附件不是立即上传的
      if(!widget.settings.ignoreReset) {
      	widget.reset();
      }
	  if(files && files.length > 0) {
	  	var ignoreConfirm = widget.settings.ignoreConfirm;
	  	if(!ignoreConfirm) {
	  		widget.settings.onDrop.call(widget.element, files, function(_files) {
				if(_files) {
					widget.queueFiles(_files);
				}
			});
	  	}else {
	  		widget.queueFiles(files);
	  	}
		$(self).val('');
	  }
    });
        
    this.settings.onInit.call(this.element);
  };

  DmUploader.prototype.reset = function()
  {
  	var widget = this;
  	// 当前正在上传,不要清空
  	if(widget.queueRunning) {
  		return;
  	}
  	widget.queuePos = -1;
    widget.queueRunning = false;
    widget.queue.splice(0, widget.queue.length);
    widget.queueMark.splice(0, widget.queueMark.length);
    widget.queueXhr.splice(0, widget.queueXhr.length);
  }
  
  DmUploader.prototype.queueFiles = function(files)
  {
    var j = this.queue.length;

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

      // Check file size
      if((this.settings.maxFileSize > 0) &&
          (file.size > this.settings.maxFileSize)){

        this.settings.onFileSizeError.call(this.element, file);

        continue;
      }
	  //alert(file.type);
	  //alert(this.settings.allowedTypes);
	  //alert(file.type.match(this.settings.allowedTypes));
      // Check file type
      if((this.settings.allowedTypes != '*') &&
          !file.type.match(this.settings.allowedTypes)){

        this.settings.onFileTypeError.call(this.element, file);

        continue;
      }

      // Check file extension
      if(this.settings.extFilter != null){
        var extList = this.settings.extFilter.toLowerCase().split(';');

        var ext = file.name.toLowerCase().split('.').pop();

        if($.inArray(ext, extList) < 0){
          this.settings.onFileExtError.call(this.element, file);

          continue;
        }
      }
            
      // Check max files
      if(this.settings.maxFiles > 0) {
        if(this.queue.length >= this.settings.maxFiles) {
          this.settings.onFilesMaxError.call(this.element, file);
		  
          continue;
        }
      }

      this.queue.push(file);
      this.queueMark.push('OK');

      var index = this.queue.length - 1;

      var isfile = this.settings.onNewFile.call(this.element, index, file);
      //return when the file is folder!!
      if(!isfile){
      	this.queue.pop(file);
      	return false;
      }
    }

    // Only start Queue if we haven't!
    if(this.queueRunning){
      return false;
    }

    // and only if new Failes were successfully added
    if(this.queue.length == j){
      return false;
    }
    // process until auto upload switch actived
    if(!!!this.settings.isAutoUploadOff){
      this.processQueue();
    }

    return true;
  };

  DmUploader.prototype.processQueue = function()
  {
    var widget = this;

    widget.queuePos++;

    if(widget.queuePos >= widget.queue.length){
      // Cleanup

      widget.settings.onComplete.call(widget.element);

      // Wait until new files are droped
      widget.queuePos = (widget.queue.length - 1);

      widget.queueRunning = false;

      return;
    }

    var file = widget.queue[widget.queuePos];
    var mark = widget.queueMark[widget.queuePos];
    if(mark !== 'OK'){
      widget.processQueue();
      return;
    }

    // Form Data
    var fd = new FormData();
    fd.append(widget.settings.fileName, file);

    // Return from client function (default === undefined)
    var can_continue = widget.settings.onBeforeUpload.call(widget.element, widget.queuePos);
    
    // If the client function doesn't return FALSE then continue
    if( false === can_continue ) {
      return;
    }

    // Append extra Form Data
    $.each(widget.settings.extraData, function(exKey, exVal){
      fd.append(exKey, exVal);
    });

    widget.queueRunning = true;
	
    // Ajax Submit
    $.ajax({
      url: widget.settings.url,
      type: widget.settings.method,
      dataType: widget.settings.dataType,
      data: fd,
      cache: false,
      contentType: false,
      processData: false,
      forceSync: false,
      xhr: function(){
        var xhrobj = $.ajaxSettings.xhr();
        widget.queueXhr[widget.queuePos] = xhrobj;
        if(xhrobj.upload){
          xhrobj.upload.addEventListener('progress', function(event) {
            var percent = 0;
            var position = event.loaded || event.position;
            var total = event.total || e.totalSize;
            if(event.lengthComputable){
              percent = Math.ceil(position / total * 100);
            }
            widget.settings.onUploadProgress.call(widget.element, widget.queuePos, percent);
          }, false);
        }

        return xhrobj;
      },
      success: function (data, message, xhr){
        widget.settings.onUploadSuccess.call(widget.element, widget.queuePos, data);
      },
      error: function (xhr, status, errMsg){
        widget.settings.onUploadError.call(widget.element, widget.queuePos, errMsg);
      },
      complete: function(xhr, textStatus){
      	widget.queueMark[widget.queuePos] = 'COMPLETE';
        widget.processQueue();
      }
    });
  }

  DmUploader.prototype.abort = function(queuePos) {
  	var widget = this;
  	var xhrObj = widget.queueXhr[queuePos];
  	var currentEle = widget.element;
	var currentPos = queuePos;
  	widget.queueMark[currentPos] = 'ABORT';
  	widget.settings.onUploadAbort.call(currentEle, currentPos);
  	if(xhrObj) {
  		// will go to error handler and complete handler
  		xhrObj.abort();
  	}
  }
  
  $.fn.dmUploader = function(options){
    return this.each(function(){
      if(!$.data(this, pluginName)){
        $.data(this, pluginName, new DmUploader(this, options));
      }
    });
  };

  // -- Disable Document D&D events to prevent opening the file on browser when we drop them
  $(document).on('dragenter', function (e) { e.stopPropagation(); e.preventDefault(); });
  $(document).on('dragover', function (e) { e.stopPropagation(); e.preventDefault(); });
  $(document).on('drop', function (e) { e.stopPropagation(); e.preventDefault(); });
})(jQuery);