"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));

var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));

var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _createSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/createSuper"));

var _tslib = require("tslib");

var _react = _interopRequireDefault(require("react"));

var _mobx = require("mobx");

var _mobxReact = require("mobx-react");

var _uniqBy = _interopRequireDefault(require("lodash/uniqBy"));

var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));

var _isPromise = _interopRequireDefault(require("is-promise"));

var _utils = require("../../../lib/upload/utils");

var _Button = _interopRequireDefault(require("../button/Button"));

var _enum = require("../button/enum");

var _autobind = _interopRequireDefault(require("../_util/autobind"));

var _FormField2 = require("../field/FormField");

var _icon = _interopRequireDefault(require("../icon"));

var _message = _interopRequireDefault(require("../message"));

var _modal = _interopRequireDefault(require("../modal"));

var _UploadList = _interopRequireDefault(require("./UploadList"));

var _Tooltip = _interopRequireDefault(require("../tooltip/Tooltip"));

var _localeContext = require("../locale-context");

var _isIE = _interopRequireDefault(require("../_util/isIE"));

/**
 * 把XMLHttpRequest对象的返回信息转化为字符串
 *
 * @param {XMLHttpRequest} xhr
 * @returns {string}
 * @memberof Upload
 */
function getResponse(xhr) {
  var res = xhr.responseText || xhr.response;

  if (!res) {
    return res;
  }

  try {
    return JSON.parse(res).message;
  } catch (e) {
    return '';
  }
}

var Upload = /*#__PURE__*/function (_FormField) {
  (0, _inherits2["default"])(Upload, _FormField);

  var _super = (0, _createSuper2["default"])(Upload);

  function Upload(props, context) {
    var _this;

    (0, _classCallCheck2["default"])(this, Upload);
    _this = _super.call(this, props, context);

    _this.saveNativeInputElement = function (elem) {
      return _this.nativeInputElement = elem;
    };
    /**
     * 传递包装按钮的点击事件
     *
     */


    _this.handleWrapperBtnClick = function () {
      _this.nativeInputElement.click();
    };

    _this.handleUploadBtnClick = function () {
      _this.startUpload();
    };
    /**
     * 文件上传前的回调
     *
     * @param {UploadFile}
     * @param {UploadFile[]}
     * @memberof Upload
     */


    _this.beforeUpload = function (file, fileList) {
      var beforeUpload = _this.props.beforeUpload;

      if (!beforeUpload) {
        return true;
      }

      var result = beforeUpload(file, fileList);

      if (result === false) {
        _this.removeFileItem(file);

        return false;
      }

      if ((0, _isPromise["default"])(result)) {
        return result;
      }

      return true;
    };

    _this.startUpload = function () {
      var fileList = (0, _toConsumableArray2["default"])(_this.fileList);

      if (fileList.length) {
        // <-- 当有文件时才上传
        _this.uploadFiles(fileList); // this.nativeInputElement.value = '';

      } else {
        _modal["default"].error((0, _localeContext.$l)('Upload', 'no_file'));
      }
    };

    _this.handleRemove = function (file) {
      // this.removeFileItem(file);
      // this.upload.abort(file);
      file.status = 'removed';

      _this.handleOnRemove(file);
    };

    (0, _mobx.runInAction)(function () {
      _this.fileList = props.uploadFileList || props.defaultFileList || [];
    });
    return _this;
  }

  (0, _createClass2["default"])(Upload, [{
    key: "componentWillReceiveProps",
    value: function componentWillReceiveProps(nextProps) {
      var uploadFileList = nextProps.uploadFileList;

      if (uploadFileList !== this.fileList && uploadFileList !== undefined) {
        this.fileList = (0, _uniqBy["default"])(uploadFileList, function (item) {
          return item.uid;
        });
      }
    }
  }, {
    key: "getOmitPropsKeys",
    value: function getOmitPropsKeys() {
      return (0, _get2["default"])((0, _getPrototypeOf2["default"])(Upload.prototype), "getOmitPropsKeys", this).call(this).concat(['accept', 'action', 'data', 'header', 'multiple', 'uploadImmediately', 'fileListMaxLength', 'showPreviewImage', 'previewImageWidth', 'showUploadBtn', 'showUploadList', 'onRemoveFile', 'onUploadSuccess', 'onUploadError', 'onFileChange', 'beforeUpload', 'withCredentials', 'partialUpload', 'appendUpload', 'uploadFileList']);
    }
  }, {
    key: "getOtherProps",
    value: function getOtherProps() {
      var otherProps = (0, _get2["default"])((0, _getPrototypeOf2["default"])(Upload.prototype), "getOtherProps", this).call(this);
      delete otherProps.ref;
      delete otherProps.onChange;
      return otherProps;
    }
  }, {
    key: "render",
    value: function render() {
      var prefixCls = this.prefixCls,
          _this$props = this.props,
          formAction = _this$props.action,
          children = _this$props.children,
          multiple = _this$props.multiple,
          accept = _this$props.accept,
          _this$props$name = _this$props.name,
          name = _this$props$name === void 0 ? 'file' : _this$props$name,
          uploadImmediately = _this$props.uploadImmediately,
          showPreviewImage = _this$props.showPreviewImage,
          previewImageWidth = _this$props.previewImageWidth,
          showUploadBtn = _this$props.showUploadBtn,
          showUploadList = _this$props.showUploadList,
          extra = _this$props.extra,
          disabled = _this$props.disabled;
      var uploadProps = (0, _objectSpread2["default"])({
        multiple: multiple,
        accept: accept ? accept.join(',') : undefined,
        action: formAction,
        name: name,
        type: 'file',
        ref: this.saveNativeInputElement,
        onChange: this.handleChange
      }, this.getOtherProps());
      var inputWrapperBtn = [/*#__PURE__*/_react["default"].createElement(_Button["default"], {
        key: "upload-btn",
        onClick: this.handleWrapperBtnClick,
        disabled: disabled
      }, /*#__PURE__*/_react["default"].createElement(_icon["default"], {
        type: "insert_drive_file"
      }), /*#__PURE__*/_react["default"].createElement("span", null, children || (0, _localeContext.$l)('Upload', 'file_selection'))), /*#__PURE__*/_react["default"].createElement("input", (0, _extends2["default"])({
        key: "upload"
      }, uploadProps, {
        hidden: true
      }))];

      var uploadBtn = /*#__PURE__*/_react["default"].createElement(_Tooltip["default"], {
        title: (0, _localeContext.$l)('Upload', 'click_to_upload'),
        placement: "right"
      }, /*#__PURE__*/_react["default"].createElement(_Button["default"], {
        color: _enum.ButtonColor.primary,
        onClick: this.handleUploadBtnClick,
        disabled: disabled
      }, /*#__PURE__*/_react["default"].createElement(_icon["default"], {
        type: "file_upload"
      })));
      /**
       * to solve the ie11 dispaly inline-block with the button cause unaligned
       */


      var IeStyle = (0, _isIE["default"])() ? {
        display: '-ms-inline-flexbox'
      } : {};
      return /*#__PURE__*/_react["default"].createElement("div", {
        className: "".concat(prefixCls),
        style: IeStyle
      }, /*#__PURE__*/_react["default"].createElement("div", {
        className: "flex-wrapper"
      }, /*#__PURE__*/_react["default"].createElement("div", {
        className: "".concat(prefixCls, "-select")
      }, inputWrapperBtn, !uploadImmediately && showUploadBtn ? uploadBtn : null), /*#__PURE__*/_react["default"].createElement("div", null, extra)), showUploadList ? /*#__PURE__*/_react["default"].createElement(_UploadList["default"], {
        previewImageWidth: previewImageWidth,
        showPreviewImage: showPreviewImage,
        items: (0, _toConsumableArray2["default"])(this.fileList),
        remove: this.handleRemove
      }) : null);
    }
    /**
     * 处理<input type="file">元素的change事件，
     * 主要是取出事件对象中的files对象并传递给uploadFiles方法
     *
     * @param {*} e <input>元素的change事件对象
     * @memberof Upload
     */

  }, {
    key: "handleChange",
    value: function handleChange(e) {
      var _this2 = this;

      if (e.target.value === '') {
        return;
      }

      var _this$props2 = this.props,
          appendUpload = _this$props2.appendUpload,
          defaultFileList = _this$props2.defaultFileList,
          uploadFileList = _this$props2.uploadFileList;
      var fileList = e.target.files;
      var files = Array.from(fileList).slice(0);
      var tempFileList = appendUpload || defaultFileList || uploadFileList ? this.fileList.slice() : [];
      var fileBuffer = [];
      files.forEach(function (file, index) {
        file.uid = _this2.getUid(index);
        file.url = URL.createObjectURL(file);

        var res = _this2.beforeUpload(file, files);

        if (!res) {
          return;
        }

        fileBuffer.push(file);
      });
      this.fileList = [].concat((0, _toConsumableArray2["default"])(tempFileList), fileBuffer);
      var _this$props3 = this.props,
          uploadImmediately = _this$props3.uploadImmediately,
          onFileChange = _this$props3.onFileChange;
      e.target.value = '';

      if (uploadImmediately) {
        if (!(0, _isEmpty["default"])(fileBuffer)) {
          this.uploadFiles(this.fileList);
        }
      }

      if (onFileChange) {
        onFileChange(this.fileList.slice());
      }
    }
    /**
     * 分别上传fileList中的每个文件对象
     *
     * @param {UploadFile[]} fileList 文件对象数组
     * @returns {void}
     * @memberof Upload
     */

  }, {
    key: "uploadFiles",
    value: function uploadFiles(fileList) {
      var _this3 = this;

      var _this$props4 = this.props,
          formAction = _this$props4.action,
          accept = _this$props4.accept,
          _this$props4$fileList = _this$props4.fileListMaxLength,
          fileListMaxLength = _this$props4$fileList === void 0 ? 0 : _this$props4$fileList,
          partialUpload = _this$props4.partialUpload;

      if (!formAction) {
        _modal["default"].error((0, _localeContext.$l)('Upload', 'upload_path_unset'));

        return;
      }

      if (!this.isAcceptFiles(fileList)) {
        _modal["default"].error((0, _localeContext.$l)('Upload', 'not_acceptable_prompt') + accept.join(','));

        return;
      }

      if (fileListMaxLength !== 0 && this.fileList.length > fileListMaxLength) {
        _modal["default"].error("".concat((0, _localeContext.$l)('Upload', 'file_list_max_length'), ": ").concat(fileListMaxLength));

        return;
      }

      var files = partialUpload ? fileList.filter(function (item) {
        return !item.status || item.status !== 'success';
      }) : Array.from(fileList); // eslint-disable-next-line @typescript-eslint/no-this-alias

      var that = this;

      if (!files.length) {
        _message["default"].info((0, _localeContext.$l)('Upload', 'been_uploaded'));
      }

      files.forEach(function (file, index) {
        file.uid = _this3.getUid(index);
        setTimeout(function () {
          that.upload(file);
        }, 0);
      });
    }
    /**
     * 上传每个文件对象
     *
     * @param {*} file
     * @returns {void}
     * @memberof Upload
     */

    /* istanbul ignore next */

  }, {
    key: "upload",
    value: function upload(file) {
      var _this4 = this;

      var _this$props5 = this.props,
          data = _this$props5.data,
          formAction = _this$props5.action,
          headers = _this$props5.headers,
          filename = _this$props5.name,
          xhrWithCredentials = _this$props5.withCredentials;

      if (typeof XMLHttpRequest === 'undefined') {
        return;
      }

      var xhr = new XMLHttpRequest();
      var formData = new FormData(); // 修改文件状态，方便UploadList判断是否展示进度条

      file.status = 'uploading';

      if (xhr.upload) {
        xhr.upload.onprogress = function (e) {
          var percent = 0;

          if (e.total > 0) {
            percent = e.loaded / e.total * 100;
          }

          _this4.handleProgress(percent, file);
        };
      }

      if (data) {
        var newData = typeof data === 'function' ? data(file) : data;
        Object.keys(newData).forEach(function (key) {
          return formData.append(key, newData[key]);
        });
      } // TODO: `filename` default value needs better implementation


      formData.append(filename || 'file', file);
      var errorMsg = "cannot post ".concat(formAction, " ").concat(xhr.status);

      if (xhrWithCredentials && 'withCredentials' in xhr) {
        xhr.withCredentials = true;
      }

      xhr.open('post', formAction, true);

      xhr.onload = function () {
        // 以二开头的状态码都认为是成功，暂定？
        var isSuccessful = xhr.status.toString().startsWith('2');

        if (isSuccessful) {
          _this4.handleSuccess(xhr.status, xhr.response, file);
        } else {
          _this4.handleError(new Error(errorMsg), getResponse(xhr), xhr.response, file);
        }
      };

      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

      if (headers !== undefined) {
        Object.keys(headers).forEach(function (key) {
          if ({}.hasOwnProperty.call(headers, key)) {
            xhr.setRequestHeader(key, headers[key]);
          }
        });
      }

      xhr.send(formData);

      xhr.onerror = function () {
        _this4.handleError(new Error(errorMsg), getResponse(xhr), xhr.response, file);
      };

      xhr.ontimeout = function () {
        var timeoutMsg = "The request post for ".concat(_mobx.action, " timed out");

        _this4.handleError(new Error(timeoutMsg), getResponse(xhr), xhr.response, file);
      };
    }
    /**
     * 处理上传成功的函数，根据结果设置文件对象的状态，
     * 用于在UploadList中的展示
     *
     * @param {number} status HTTP状态码
     * @param {string} response 响应对象
     * @param {UploadFile} file 文件对象
     * @returns
     */

  }, {
    key: "handleSuccess",
    value: function handleSuccess(status, response, file) {
      var targetItem = this.getFileItem(file);

      if (targetItem) {
        var onUploadSuccess = this.props.onUploadSuccess;
        targetItem.status = status === 200 ? 'success' : 'done';
        targetItem.response = response;

        if (onUploadSuccess) {
          onUploadSuccess(response, file);
        }

        this.forceUpdate();
      }
    }
    /**
     * 处理上传进度变化的函数，更新文件对象中的percent值，
     * 用于在UploadList中展示
     *
     * @param {number} percent 上传百分比
     * @param {UploadFile} file 文件对象
     * @returns
     */

  }, {
    key: "handleProgress",
    value: function handleProgress(percent, file) {
      var onUploadProgress = this.props.onUploadProgress;
      var targetItem = this.getFileItem(file);

      if (targetItem) {
        targetItem.percent = percent;

        if (onUploadProgress) {
          onUploadProgress(percent, file);
        }

        this.forceUpdate();
      }
    }
    /**
     * 处理上传出错的函数，用于设置文件对象的status值，
     *
     * @param {Error} error 错误对象
     * @param {string} responseText 处理成字符串的响应对象
     * @param {UploadFile} file 文件对象
     * @returns
     */

  }, {
    key: "handleError",
    value: function handleError(error, responseText, response, file) {
      var onUploadError = this.props.onUploadError;
      var targetItem = this.getFileItem(file);

      if (targetItem) {
        targetItem.status = 'error';
        targetItem.error = error;
        targetItem.response = responseText;

        if (onUploadError) {
          onUploadError(error, response, file);
        }

        this.forceUpdate();
      }
    }
  }, {
    key: "handleOnRemove",
    value: function handleOnRemove(file) {
      var _this5 = this;

      var onRemoveFile = this.props.onRemoveFile;
      Promise.resolve(typeof onRemoveFile === 'function' ? onRemoveFile(file) : onRemoveFile).then(function (ret) {
        if (ret === false) {
          return;
        }

        _this5.removeFileItem(file);
      });
    }
    /**
     * 判断文件后缀名是否合格
     * this.props.accept值为falsy时返回true，否则正常判断
     *
     * @param {UploadFile[]} fileList 文件对象数组
     * @returns {boolean}
     * @memberof Upload
     */

  }, {
    key: "isAcceptFiles",
    value: function isAcceptFiles(fileList) {
      var accept = this.props.accept;

      if (!accept) {
        return true;
      }

      var acceptTypes = accept.map(function (type) {
        type = type.replace(/\./g, '\\.');
        type = type.replace(/\*/g, '.*');
        return new RegExp(type);
      });
      return fileList.some(function (_ref) {
        var name = _ref.name,
            type = _ref.type;
        return acceptTypes.some(function (acceptType) {
          return acceptType.test(name) || acceptType.test(type);
        });
      });
    }
    /**
     * 使用日期获取一个uid
     *
     * @param {number} index 索引
     * @returns {string}
     * @memberof Upload
     */

  }, {
    key: "getUid",
    value: function getUid(index) {
      var prefixCls = this.prefixCls;
      var now = new Date();
      return "".concat(prefixCls, "-").concat(now, "-").concat(index);
    }
    /**
     * 从文件对象数组中获取一个文件对象的引用，
     * 首先尝试通过uid属性匹配文件对象，若失败则尝试name
     *
     * @param {UploadFile} file
     * @param {UploadFile[]} fileList 文件对象数组
     * @returns {UploadFile}
     * @memberof Upload
     */

  }, {
    key: "getFileItem",
    value: function getFileItem(file) {
      var matchKey = file.uid !== undefined ? 'uid' : 'name';
      return this.fileList.find(function (item) {
        return item[matchKey] === file[matchKey];
      });
    }
    /**
     * 从文件对象数组中移除一个文件对象，
     * 首先尝试通过uid属性匹配文件对象，若失败则尝试name
     *
     * @param {UploadFile} file
     * @param {UploadFile[]} fileList
     * @returns {UploadFile[]}
     * @memberof Upload
     */

  }, {
    key: "removeFileItem",
    value: function removeFileItem(file) {
      var matchKey = file.uid !== undefined ? 'uid' : 'name';
      var index = this.fileList.findIndex(function (item) {
        return item[matchKey] === file[matchKey];
      });
      var uploadFileList = this.props.uploadFileList;

      if (uploadFileList && uploadFileList.length) {
        uploadFileList.splice(index, 1);
        this.fileList.splice(index, 1);
      } else {
        this.fileList.splice(index, 1);
      }
    }
  }]);
  return Upload;
}(_FormField2.FormField);

Upload.displayName = 'Upload';
Upload.defaultProps = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, _FormField2.FormField.defaultProps), {}, {
  suffixCls: 'upload',
  multiple: false,
  headers: {},
  data: {},
  action: '',
  name: 'file',
  withCredentials: false,
  appendUpload: false,
  partialUpload: true,
  uploadImmediately: true,
  fileListMaxLength: 0,
  showPreviewImage: true,
  previewImageWidth: 100,
  showUploadBtn: true,
  showUploadList: true,
  beforeUpload: _utils.T,
  onUploadSuccess: function onUploadSuccess() {
    _message["default"].success((0, _localeContext.$l)('Upload', 'upload_success'));
  },
  onUploadError: function onUploadError() {
    _message["default"].error((0, _localeContext.$l)('Upload', 'upload_failure'));
  }
});
(0, _tslib.__decorate)([_mobx.observable], Upload.prototype, "fileList", void 0);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "componentWillReceiveProps", null);
(0, _tslib.__decorate)([_autobind["default"], _mobx.action], Upload.prototype, "handleChange", null);
(0, _tslib.__decorate)([_autobind["default"], _mobx.action], Upload.prototype, "uploadFiles", null);
(0, _tslib.__decorate)([_autobind["default"], _mobx.action], Upload.prototype, "upload", null);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "handleSuccess", null);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "handleProgress", null);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "handleError", null);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "handleRemove", void 0);
(0, _tslib.__decorate)([_mobx.action], Upload.prototype, "removeFileItem", null);
Upload = (0, _tslib.__decorate)([_mobxReact.observer], Upload);
var _default = Upload;
exports["default"] = _default;
//# sourceMappingURL=Upload.js.map
