/**

 @Title: layui.upload 文件上传
 @Author: 贤心
 @License：MIT

 */
 
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;
	 //初始化，只追加本页面用到的上传方法
	 if($(that.config.elem.selector).length>0){
		 var 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="'+ options.method
		  ,'" 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_IFRAME)){
			//判断上传文件域，通过id的button,用form包裹文件域，通过class的button，用form把class所在包裹起来
			var elemfile = that.elemFile.parent(".layui-upload-wrap").find('button').attr('id');
			if(elemfile){
				that.elemFile.wrap(elemForm);
			}else{
				//$("."+ELEM_FILE).wrap(elemForm);
				//只有通过class的才包裹
				if(options.elem.selector.indexOf(".")!=-1){
					var selector = $(".layui-upload-wrap").find(options.elem.selector);
					if(selector.length>0){
						$(options.elem.selector).parent(".layui-upload-wrap").find("."+ELEM_FILE).wrap(elemForm);
					}
				}
			} 
			//追加额外的参数
			options.elem.next('.'+ ELEM_FORM).append(function(){
				var arr = [];
				//var path = options.elem.attr('data-path');
				layui.each(options.data, function(key, value){
//					if(key=='path' && path){
//						value = path;
//					}else{
						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);
        });
        
        //提交文件
        $.ajax({
          url: options.url
          ,type: '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();
          }
        });
      });
    }
    
    //低版本IE处理方式，不支持跨域
    ,iframeSend = function(){
		// ie8/9追加额外的参数,由于是在选择图片时获取的，故需在上传前处理
		if(!options.elem.next().hasClass(ELEM_IFRAME)){
			
			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('');
			}());
		}

		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;
        }
      }
      
      //上传前的回调
      options.before && options.before(args);

      //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
      ,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');
	  // 上传图片才调用已有的choose
      if(that.isFile() || (options.choose && options.accept == 'images')) return;
      elemFile.after('<span class="layui-inline '+ ELEM_CHOOSE +'">'+ value +'</span>');
    };

    //解决ie8/9, 无法获取lay-data值的问题 
	if(device.ie && device.ie < 10){
		if($(options.elem.selector).length>0 && options.elem.selector.indexOf(".")!=-1){
			$("."+ELEM_FILE).click(function(){
				var othis = $(this),data=$(this).parents('.layui-upload-wrap').find('button').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.elemFile = {};
				that.config.item = that.elemFile = othis;
				
				that.elemFile.change(function(){
					if(that.config.imgid){
						var imgid = document.getElementById(that.config.imgid);
						this.select(); 
						
						//在file控件下获取焦点情况下 document.selection.createRange() 将会拒绝访问 ，需要失去焦点
						if(that.config.source && that.config.source == 'back'){
							//后台的iframe中失去焦点
							window.parent.document.body.focus();  
						}else{
							//非iframe中用此
							this.blur();  
						}

						//获取文本内容值，在IE8中,选择文件之后,显示文件的本地的路径 
						var imgSrc = document.selection.createRange().text; 
						imgid.setAttribute('src','');
						imgid.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src=\"" + imgSrc + "\")";
					}
					
					if (that.config.parentid && $('#'+that.config.parentid).length>0){
						// 图片有外层div的
						$('#'+that.config.parentid).removeClass('none');
					}else{
						// img隐藏的，预览时要显示出来
						$('#'+that.config.imgid).removeClass('none');
					}
					// 自动上传的，才执行上传操作
					if(that.config.auto){
						that.upload();
					}
				});
				
			})
		}
	}else{
		//点击上传容器
		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();
		});
	}
    
    //拖拽上传，不支持ie8/9
    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);
        }
      });
    }
	//文件选择, ie8/9上面已有新的处理
    if(!(device.ie && device.ie < 10)){
		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);
});

