text: 
  /*!
   * upload 文件上传组件
   * MIT Licensed
   */
   
  layui.define('layer' , function(exports){
    "use strict";
    
    var $ = layui.$
    ,layer = layui.layer
    ,hint = layui.hint()
    ,device = layui.device()
  
    //外部接口
    ,upload = {
      config: {} //全局配置项
  
      //设置全局项
      ,set: function(options){
        var that = this;
        that.config = $.extend({}, that.config, options);
        return that;
      }
      
      //事件
      ,on: function(events, callback){
        return layui.onevent.call(this, MOD_NAME, events, callback);
      }
    }
    
    //操作当前实例
    ,thisUpload = function(){
      var that = this;
      return {
        upload: function(files){
          that.upload.call(that, files);
        }
        ,reload: function(options){
          that.reload.call(that, options);
        }
        ,config: that.config
      }
    }
    
    //字符常量
    ,MOD_NAME = 'upload', ELEM = 'layui-upload', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled'
    
    ,ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form', ELEM_IFRAME = 'layui-upload-iframe', ELEM_CHOOSE = 'layui-upload-choose', ELEM_DRAG = 'layui-upload-drag'
    
    
    //构造器
    ,Class = function(options){
      var that = this;
      that.config = $.extend({}, that.config, upload.config, options);
      that.render();
    };
    
    //默认配置
    Class.prototype.config = {
      accept: 'images' //允许上传的文件类型：images/file/video/audio
      ,exts: '' //允许上传的文件后缀名
      ,auto: true //是否选完文件后自动上传
      ,bindAction: '' //手动上传触发的元素
      ,url: '' //上传地址
      ,field: 'file' //文件字段名
      ,acceptMime: '' //筛选出的文件类型，默认为所有文件
      ,method: 'post' //请求上传的 http 类型
      ,data: {} //请求上传的额外参数
      ,drag: true //是否允许拖拽上传
      ,size: 0 //文件限制大小，默认不限制
      ,number: 0 //允许同时上传的文件数，默认不限制
      ,multiple: false //是否允许多文件上传，不支持ie8-9
    };
    
    //初始渲染
    Class.prototype.render = function(options){
      var that = this
      ,options = that.config;
  
      options.elem = $(options.elem);
      options.bindAction = $(options.bindAction);
  
      that.file();
      that.events();
    };
    
    //追加文件域
    Class.prototype.file = function(){
      var that = this
      ,options = that.config
      ,elemFile = that.elemFile = $([
        '<input class="'+ ELEM_FILE +'" type="file" accept="'+ options.acceptMime +'" name="'+ options.field +'"'
        ,(options.multiple ? ' multiple' : '') 
        ,'>'
      ].join(''))
      ,next = options.elem.next();
      
      if(next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)){
        next.remove();
      }
      
      //包裹ie8/9容器
      if(device.ie && device.ie < 10){
        options.elem.wrap('<div class="layui-upload-wrap"></div>');
      }
      
      that.isFile() ? (
        that.elemFile = options.elem
        ,options.field = options.elem[0].name
      ) : options.elem.after(elemFile);
      
      //初始化ie8/9的Form域
      if(device.ie && device.ie < 10){
        that.initIE();
      }
    };
    
    //ie8-9初始化
    Class.prototype.initIE = function(){
      var that = this
      ,options = that.config
      ,iframe = $('<iframe id="'+ ELEM_IFRAME +'" class="'+ ELEM_IFRAME +'" name="'+ ELEM_IFRAME +'" frameborder="0"></iframe>')
      ,elemForm = $(['<form target="'+ ELEM_IFRAME +'" class="'+ ELEM_FORM +'" method="post" key="set-mine" enctype="multipart/form-data" action="'+ options.url +'">'
      ,'</form>'].join(''));
      
      //插入iframe    
      $('#'+ ELEM_IFRAME)[0] || $('body').append(iframe);
  
      //包裹文件域
      if(!options.elem.next().hasClass(ELEM_FORM)){
        that.elemFile.wrap(elemForm);      
        
        //追加额外的参数
        options.elem.next('.'+ ELEM_FORM).append(function(){
          var arr = [];
          layui.each(options.data, function(key, value){
            value = typeof value === 'function' ? value() : value;
            arr.push('<input type="hidden" name="'+ key +'" value="'+ value +'">')
          });
          return arr.join('');
        }());
      }
    };
    
    //异常提示
    Class.prototype.msg = function(content){
      return layer.msg(content, {
        icon: 2
        ,shift: 6
      });
    };
    
    //判断绑定元素是否为文件域本身
    Class.prototype.isFile = function(){
      var elem = this.config.elem[0];
      if(!elem) return;
      return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file'
    }
    
    //预读图片信息
    Class.prototype.preview = function(callback){
      var that = this;
      if(window.FileReader){
        layui.each(that.chooseFiles, function(index, file){
          var reader = new FileReader();
          reader.readAsDataURL(file);  
          reader.onload = function(){
            callback && callback(index, file, this.result);
          }
        });
      }
    };
    
    //执行上传
    Class.prototype.upload = function(files, type){
      var that = this
      ,options = that.config
      ,elemFile = that.elemFile[0]
      
      //高级浏览器处理方式，支持跨域
      ,ajaxSend = function(){
        var successful = 0, aborted = 0
        ,items = files || that.files || that.chooseFiles || elemFile.files
        ,allDone = function(){ //多文件全部上传完毕的回调
          if(options.multiple && successful + aborted === that.fileLength){
            typeof options.allDone === 'function' && options.allDone({
              total: that.fileLength
              ,successful: successful
              ,aborted: aborted
            });
          }
        };
        layui.each(items, function(index, file){
          var formData = new FormData();
          
          formData.append(options.field, file);
          
          //追加额外的参数
          layui.each(options.data, function(key, value){
            value = typeof value === 'function' ? value() : value;
            formData.append(key, value);
          });
          
          //提交文件
          var opts = {
            url: options.url
            ,type: 'post' //统一采用 post 上传
            ,data: formData
            ,contentType: false 
            ,processData: false
            ,dataType: 'json'
            ,headers: options.headers || {}
            //成功回调
            ,success: function(res){
              successful++;
              done(index, res);
              allDone();
            }
            //异常回调
            ,error: function(){
              aborted++;
              that.msg('请求上传接口出现异常');
              error(index);
              allDone();
            }
          };
          //进度条
          if(typeof options.progress === 'function'){
            opts.xhr = function(){
              var xhr = $.ajaxSettings.xhr();
              //上传进度
              xhr.upload.addEventListener("progress", function (obj) {
                if(obj.lengthComputable){
                  var percent = Math.floor((obj.loaded/obj.total)* 100); //百分比
                  options.progress(percent, (options.item ? options.item[0] : options.elem[0]) , obj, index);
                }
              });
              return xhr;
            }
          }
          $.ajax(opts);
        });
      }
      
      //低版本IE处理方式，不支持跨域
      ,iframeSend = function(){
        var iframe = $('#'+ ELEM_IFRAME);
      
        that.elemFile.parent().submit();
  
        //获取响应信息
        clearInterval(Class.timer);
        Class.timer = setInterval(function() {
          var res, iframeBody = iframe.contents().find('body');
          try {
            res = iframeBody.text();
          } catch(e) {
            that.msg('获取上传后的响应信息出现异常');
            clearInterval(Class.timer);
            error();
          }
          if(res){
            clearInterval(Class.timer);
            iframeBody.html('');
            done(0, res);
          }
        }, 30); 
      }
      
      //统一回调
      ,done = function(index, res){
        that.elemFile.next('.'+ ELEM_CHOOSE).remove();
        elemFile.value = '';
        if(typeof res !== 'object'){
          try {
            res = JSON.parse(res);
          } catch(e){
            res = {};
            return that.msg('请对上传接口返回有效JSON');
          }
        }
        typeof options.done === 'function' && options.done(res, index || 0, function(files){
          that.upload(files);
        });
      }
      
      //统一网络异常回调
      ,error = function(index){
        if(options.auto){
          elemFile.value = '';
        }
        typeof options.error === 'function' && options.error(index || 0, function(files){
          that.upload(files);
        });
      }
      
      ,exts = options.exts
      ,check ,value = function(){
        var arr = [];
        layui.each(files || that.chooseFiles, function(i, item){
          arr.push(item.name);
        });
        return arr;
      }()
      
      //回调返回的参数
      ,args = {
        //预览
        preview: function(callback){
          that.preview(callback);
        }
        //上传
        ,upload: function(index, file){
          var thisFile = {};
          thisFile[index] = file;
          that.upload(thisFile);
        }
        //追加文件到队列
        ,pushFile: function(){
          that.files = that.files || {};
          layui.each(that.chooseFiles, function(index, item){
            that.files[index] = item;
          });
          return that.files;
        }
        //重置文件
        ,resetFile: function(index, file, filename){
          var newFile = new File([file], filename);
          that.files = that.files || {};
          that.files[index] = newFile;
        }
      }
      
      //提交上传
      ,send = function(){      
        //选择文件的回调      
        if(type === 'choose' || options.auto){
          options.choose && options.choose(args);
          if(type === 'choose'){
            return;
          }
        }
        
        //上传前的回调 - 如果回调函数明确返回false，则停止上传(#pulls55)
        if(options.before && (options.before(args) === false)) return;
  
        //IE兼容处理
        if(device.ie){
          return device.ie > 9 ? ajaxSend() : iframeSend();
        }
        
        ajaxSend();
      }
  
      //校验文件格式
      value = value.length === 0 
        ? ((elemFile.value.match(/[^\/\\]+\..+/g)||[]) || '')
      : value;
      
      if(value.length === 0) return;
  
      switch(options.accept){
        case 'file': //一般文件
          if(exts && !RegExp('\\w\\.('+ exts +')$', 'i').test(escape(value))){
            that.msg('选择的文件中包含不支持的格式');
            return elemFile.value = '';
          }
        break;
        case 'video': //视频文件
          if(!RegExp('\\w\\.('+ (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') +')$', 'i').test(escape(value))){
            that.msg('选择的视频中包含不支持的格式');
            return elemFile.value = '';
          }
        break;
        case 'audio': //音频文件
          if(!RegExp('\\w\\.('+ (exts || 'mp3|wav|mid') +')$', 'i').test(escape(value))){
            that.msg('选择的音频中包含不支持的格式');
            return elemFile.value = '';
          }
        break;
        default: //图片文件
          layui.each(value, function(i, item){
            if(!RegExp('\\w\\.('+ (exts || 'jpg|png|gif|bmp|jpeg$') +')', 'i').test(escape(item))){
              check = true;
            }
          });
          if(check){
            that.msg('选择的图片中包含不支持的格式');
            return elemFile.value = '';
          }
        break;
      }
      
      //检验文件数量
      that.fileLength = function(){
        var length = 0
        ,items = files || that.files || that.chooseFiles || elemFile.files;
        layui.each(items, function(){
          length++;
        });
        return length;
      }();
      if(options.number && that.fileLength > options.number){
        return that.msg('同时最多只能上传的数量为：'+ options.number);
      }
      
      //检验文件大小
      if(options.size > 0 && !(device.ie && device.ie < 10)){
        var limitSize;
        
        layui.each(that.chooseFiles, function(index, file){
          if(file.size > 1024*options.size){
            var size = options.size/1024;
            size = size >= 1 ? (size.toFixed(2) + 'MB') : options.size + 'KB'
            elemFile.value = '';
            limitSize = size;
          }
        });
        if(limitSize) return that.msg('文件不能超过'+ limitSize);
      }
      send();
    };
    
    //重置方法
    Class.prototype.reload = function(options){
      options = options || {};
      delete options.elem;
      delete options.bindAction;
      
      var that = this
      ,options = that.config = $.extend({}, that.config, upload.config, options)
      ,next = options.elem.next();
      
      //更新文件域相关属性
      next.attr({
        name: options.name
        ,accept: options.acceptMime
        ,multiple: options.multiple
      });
    };
    
    //事件处理
    Class.prototype.events = function(){
      var that = this
      ,options = that.config
      
      //设置当前选择的文件队列
      ,setChooseFile = function(files){
        that.chooseFiles = {};
        layui.each(files, function(i, item){
          var time = new Date().getTime();
          that.chooseFiles[time + '-' + i] = item;
        });
      }
      
      //设置选择的文本
      ,setChooseText = function(files, filename){
        var elemFile = that.elemFile
        ,item = options.item ? options.item : options.elem
        ,value = files.length > 1 
          ? files.length + '个文件' 
        : ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g)||[]) || '');
        
        if(elemFile.next().hasClass(ELEM_CHOOSE)){
          elemFile.next().remove();
        }
        that.upload(null, 'choose');
        if(that.isFile() || options.choose) return;
        elemFile.after('<span class="layui-inline '+ ELEM_CHOOSE +'">'+ value +'</span>');
      };
  
      //点击上传容器
      options.elem.off('upload.start').on('upload.start', function(){
        var othis = $(this), data = othis.attr('lay-data');
        
        if(data){
          try{
            data = new Function('return '+ data)();
            that.config = $.extend({}, options, data);
          } catch(e){
            hint.error('Upload element property lay-data configuration item has a syntax error: ' + data)
          }
        }
        
        that.config.item = othis;
        that.elemFile[0].click();
      });
      
      //拖拽上传
      if(!(device.ie && device.ie < 10)){
        options.elem.off('upload.over').on('upload.over', function(){
          var othis = $(this)
          othis.attr('lay-over', '');
        })
        .off('upload.leave').on('upload.leave', function(){
          var othis = $(this)
          othis.removeAttr('lay-over');
        })
        .off('upload.drop').on('upload.drop', function(e, param){
          var othis = $(this), files = param.originalEvent.dataTransfer.files || [];
          
          othis.removeAttr('lay-over');
          setChooseFile(files);
          
          if(options.auto){
            that.upload(files);
          } else {
            setChooseText(files);
          }
        });
      }
      
      //文件选择
      that.elemFile.off('upload.change').on('upload.change', function(){
        var files = this.files || [];
        setChooseFile(files);
        options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
      });
      
      //手动触发上传
      options.bindAction.off('upload.action').on('upload.action', function(){
        that.upload();
      });
      
      //防止事件重复绑定
      if(options.elem.data('haveEvents')) return;
      
      that.elemFile.on('change', function(){
        $(this).trigger('upload.change');
      });
      
      options.elem.on('click', function(){
        if(that.isFile()) return;
        $(this).trigger('upload.start');
      });
      
      if(options.drag){
        options.elem.on('dragover', function(e){
          e.preventDefault();
          $(this).trigger('upload.over');
        }).on('dragleave', function(e){
          $(this).trigger('upload.leave');
        }).on('drop', function(e){
          e.preventDefault();
          $(this).trigger('upload.drop', e);
        });
      }
      
      options.bindAction.on('click', function(){
        $(this).trigger('upload.action');
      });
      
      options.elem.data('haveEvents', true);
    };
    
    //核心入口  
    upload.render = function(options){
      var inst = new Class(options);
      return thisUpload.call(inst);
    };
    
    exports(MOD_NAME, upload);
  });
  
  