668 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			668 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";
 | 
						||
 | 
						||
var _nonSecure = require("nanoid/non-secure");
 | 
						||
 | 
						||
var _companionClient = require("@uppy/companion-client");
 | 
						||
 | 
						||
var _RateLimitedQueue = require("@uppy/utils/lib/RateLimitedQueue");
 | 
						||
 | 
						||
const BasePlugin = require("@uppy/core/lib/BasePlugin");
 | 
						||
 | 
						||
const emitSocketProgress = require("@uppy/utils/lib/emitSocketProgress");
 | 
						||
 | 
						||
const getSocketHost = require("@uppy/utils/lib/getSocketHost");
 | 
						||
 | 
						||
const settle = require("@uppy/utils/lib/settle");
 | 
						||
 | 
						||
const EventTracker = require("@uppy/utils/lib/EventTracker");
 | 
						||
 | 
						||
const ProgressTimeout = require("@uppy/utils/lib/ProgressTimeout");
 | 
						||
 | 
						||
const NetworkError = require("@uppy/utils/lib/NetworkError");
 | 
						||
 | 
						||
const isNetworkError = require("@uppy/utils/lib/isNetworkError");
 | 
						||
 | 
						||
const packageJson = {
 | 
						||
  "version": "2.1.3"
 | 
						||
};
 | 
						||
 | 
						||
const locale = require("./locale.js");
 | 
						||
 | 
						||
function buildResponseError(xhr, err) {
 | 
						||
  let error = err; // No error message
 | 
						||
 | 
						||
  if (!error) error = new Error('Upload error'); // Got an error message string
 | 
						||
 | 
						||
  if (typeof error === 'string') error = new Error(error); // Got something else
 | 
						||
 | 
						||
  if (!(error instanceof Error)) {
 | 
						||
    error = Object.assign(new Error('Upload error'), {
 | 
						||
      data: error
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  if (isNetworkError(xhr)) {
 | 
						||
    error = new NetworkError(error, xhr);
 | 
						||
    return error;
 | 
						||
  }
 | 
						||
 | 
						||
  error.request = xhr;
 | 
						||
  return error;
 | 
						||
}
 | 
						||
/**
 | 
						||
 * Set `data.type` in the blob to `file.meta.type`,
 | 
						||
 * because we might have detected a more accurate file type in Uppy
 | 
						||
 * https://stackoverflow.com/a/50875615
 | 
						||
 *
 | 
						||
 * @param {object} file File object with `data`, `size` and `meta` properties
 | 
						||
 * @returns {object} blob updated with the new `type` set from `file.meta.type`
 | 
						||
 */
 | 
						||
 | 
						||
 | 
						||
function setTypeInBlob(file) {
 | 
						||
  const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type);
 | 
						||
  return dataWithUpdatedType;
 | 
						||
}
 | 
						||
 | 
						||
class XHRUpload extends BasePlugin {
 | 
						||
  // eslint-disable-next-line global-require
 | 
						||
  constructor(uppy, opts) {
 | 
						||
    super(uppy, opts);
 | 
						||
    this.type = 'uploader';
 | 
						||
    this.id = this.opts.id || 'XHRUpload';
 | 
						||
    this.title = 'XHRUpload';
 | 
						||
    this.defaultLocale = locale; // Default options
 | 
						||
 | 
						||
    const defaultOptions = {
 | 
						||
      formData: true,
 | 
						||
      fieldName: opts.bundle ? 'files[]' : 'file',
 | 
						||
      method: 'post',
 | 
						||
      metaFields: null,
 | 
						||
      responseUrlFieldName: 'url',
 | 
						||
      bundle: false,
 | 
						||
      headers: {},
 | 
						||
      timeout: 30 * 1000,
 | 
						||
      limit: 5,
 | 
						||
      withCredentials: false,
 | 
						||
      responseType: '',
 | 
						||
 | 
						||
      /**
 | 
						||
       * @param {string} responseText the response body string
 | 
						||
       */
 | 
						||
      getResponseData(responseText) {
 | 
						||
        let parsedResponse = {};
 | 
						||
 | 
						||
        try {
 | 
						||
          parsedResponse = JSON.parse(responseText);
 | 
						||
        } catch (err) {
 | 
						||
          uppy.log(err);
 | 
						||
        }
 | 
						||
 | 
						||
        return parsedResponse;
 | 
						||
      },
 | 
						||
 | 
						||
      /**
 | 
						||
       *
 | 
						||
       * @param {string} _ the response body string
 | 
						||
       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
 | 
						||
       */
 | 
						||
      getResponseError(_, response) {
 | 
						||
        let error = new Error('Upload error');
 | 
						||
 | 
						||
        if (isNetworkError(response)) {
 | 
						||
          error = new NetworkError(error, response);
 | 
						||
        }
 | 
						||
 | 
						||
        return error;
 | 
						||
      },
 | 
						||
 | 
						||
      /**
 | 
						||
       * Check if the response from the upload endpoint indicates that the upload was successful.
 | 
						||
       *
 | 
						||
       * @param {number} status the response status code
 | 
						||
       */
 | 
						||
      validateStatus(status) {
 | 
						||
        return status >= 200 && status < 300;
 | 
						||
      }
 | 
						||
 | 
						||
    };
 | 
						||
    this.opts = { ...defaultOptions,
 | 
						||
      ...opts
 | 
						||
    };
 | 
						||
    this.i18nInit();
 | 
						||
    this.handleUpload = this.handleUpload.bind(this); // Simultaneous upload limiting is shared across all uploads with this plugin.
 | 
						||
 | 
						||
    if (_RateLimitedQueue.internalRateLimitedQueue in this.opts) {
 | 
						||
      this.requests = this.opts[_RateLimitedQueue.internalRateLimitedQueue];
 | 
						||
    } else {
 | 
						||
      this.requests = new _RateLimitedQueue.RateLimitedQueue(this.opts.limit);
 | 
						||
    }
 | 
						||
 | 
						||
    if (this.opts.bundle && !this.opts.formData) {
 | 
						||
      throw new Error('`opts.formData` must be true when `opts.bundle` is enabled.');
 | 
						||
    }
 | 
						||
 | 
						||
    this.uploaderEvents = Object.create(null);
 | 
						||
  }
 | 
						||
 | 
						||
  getOptions(file) {
 | 
						||
    const overrides = this.uppy.getState().xhrUpload;
 | 
						||
    const {
 | 
						||
      headers
 | 
						||
    } = this.opts;
 | 
						||
    const opts = { ...this.opts,
 | 
						||
      ...(overrides || {}),
 | 
						||
      ...(file.xhrUpload || {}),
 | 
						||
      headers: {}
 | 
						||
    }; // Support for `headers` as a function, only in the XHRUpload settings.
 | 
						||
    // Options set by other plugins in Uppy state or on the files themselves are still merged in afterward.
 | 
						||
    //
 | 
						||
    // ```js
 | 
						||
    // headers: (file) => ({ expires: file.meta.expires })
 | 
						||
    // ```
 | 
						||
 | 
						||
    if (typeof headers === 'function') {
 | 
						||
      opts.headers = headers(file);
 | 
						||
    } else {
 | 
						||
      Object.assign(opts.headers, this.opts.headers);
 | 
						||
    }
 | 
						||
 | 
						||
    if (overrides) {
 | 
						||
      Object.assign(opts.headers, overrides.headers);
 | 
						||
    }
 | 
						||
 | 
						||
    if (file.xhrUpload) {
 | 
						||
      Object.assign(opts.headers, file.xhrUpload.headers);
 | 
						||
    }
 | 
						||
 | 
						||
    return opts;
 | 
						||
  } // eslint-disable-next-line class-methods-use-this
 | 
						||
 | 
						||
 | 
						||
  addMetadata(formData, meta, opts) {
 | 
						||
    const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields : Object.keys(meta); // Send along all fields by default.
 | 
						||
 | 
						||
    metaFields.forEach(item => {
 | 
						||
      formData.append(item, meta[item]);
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  createFormDataUpload(file, opts) {
 | 
						||
    const formPost = new FormData();
 | 
						||
    this.addMetadata(formPost, file.meta, opts);
 | 
						||
    const dataWithUpdatedType = setTypeInBlob(file);
 | 
						||
 | 
						||
    if (file.name) {
 | 
						||
      formPost.append(opts.fieldName, dataWithUpdatedType, file.meta.name);
 | 
						||
    } else {
 | 
						||
      formPost.append(opts.fieldName, dataWithUpdatedType);
 | 
						||
    }
 | 
						||
 | 
						||
    return formPost;
 | 
						||
  }
 | 
						||
 | 
						||
  createBundledUpload(files, opts) {
 | 
						||
    const formPost = new FormData();
 | 
						||
    const {
 | 
						||
      meta
 | 
						||
    } = this.uppy.getState();
 | 
						||
    this.addMetadata(formPost, meta, opts);
 | 
						||
    files.forEach(file => {
 | 
						||
      const options = this.getOptions(file);
 | 
						||
      const dataWithUpdatedType = setTypeInBlob(file);
 | 
						||
 | 
						||
      if (file.name) {
 | 
						||
        formPost.append(options.fieldName, dataWithUpdatedType, file.name);
 | 
						||
      } else {
 | 
						||
        formPost.append(options.fieldName, dataWithUpdatedType);
 | 
						||
      }
 | 
						||
    });
 | 
						||
    return formPost;
 | 
						||
  }
 | 
						||
 | 
						||
  upload(file, current, total) {
 | 
						||
    const opts = this.getOptions(file);
 | 
						||
    this.uppy.log(`uploading ${current} of ${total}`);
 | 
						||
    return new Promise((resolve, reject) => {
 | 
						||
      this.uppy.emit('upload-started', file);
 | 
						||
      const data = opts.formData ? this.createFormDataUpload(file, opts) : file.data;
 | 
						||
      const xhr = new XMLHttpRequest();
 | 
						||
      this.uploaderEvents[file.id] = new EventTracker(this.uppy);
 | 
						||
      let queuedRequest;
 | 
						||
      const timer = new ProgressTimeout(opts.timeout, () => {
 | 
						||
        xhr.abort();
 | 
						||
        queuedRequest.done();
 | 
						||
        const error = new Error(this.i18n('timedOut', {
 | 
						||
          seconds: Math.ceil(opts.timeout / 1000)
 | 
						||
        }));
 | 
						||
        this.uppy.emit('upload-error', file, error);
 | 
						||
        reject(error);
 | 
						||
      });
 | 
						||
      const id = (0, _nonSecure.nanoid)();
 | 
						||
      xhr.upload.addEventListener('loadstart', () => {
 | 
						||
        this.uppy.log(`[XHRUpload] ${id} started`);
 | 
						||
      });
 | 
						||
      xhr.upload.addEventListener('progress', ev => {
 | 
						||
        this.uppy.log(`[XHRUpload] ${id} progress: ${ev.loaded} / ${ev.total}`); // Begin checking for timeouts when progress starts, instead of loading,
 | 
						||
        // to avoid timing out requests on browser concurrency queue
 | 
						||
 | 
						||
        timer.progress();
 | 
						||
 | 
						||
        if (ev.lengthComputable) {
 | 
						||
          this.uppy.emit('upload-progress', file, {
 | 
						||
            uploader: this,
 | 
						||
            bytesUploaded: ev.loaded,
 | 
						||
            bytesTotal: ev.total
 | 
						||
          });
 | 
						||
        }
 | 
						||
      });
 | 
						||
      xhr.addEventListener('load', () => {
 | 
						||
        this.uppy.log(`[XHRUpload] ${id} finished`);
 | 
						||
        timer.done();
 | 
						||
        queuedRequest.done();
 | 
						||
 | 
						||
        if (this.uploaderEvents[file.id]) {
 | 
						||
          this.uploaderEvents[file.id].remove();
 | 
						||
          this.uploaderEvents[file.id] = null;
 | 
						||
        }
 | 
						||
 | 
						||
        if (opts.validateStatus(xhr.status, xhr.responseText, xhr)) {
 | 
						||
          const body = opts.getResponseData(xhr.responseText, xhr);
 | 
						||
          const uploadURL = body[opts.responseUrlFieldName];
 | 
						||
          const uploadResp = {
 | 
						||
            status: xhr.status,
 | 
						||
            body,
 | 
						||
            uploadURL
 | 
						||
          };
 | 
						||
          this.uppy.emit('upload-success', file, uploadResp);
 | 
						||
 | 
						||
          if (uploadURL) {
 | 
						||
            this.uppy.log(`Download ${file.name} from ${uploadURL}`);
 | 
						||
          }
 | 
						||
 | 
						||
          return resolve(file);
 | 
						||
        }
 | 
						||
 | 
						||
        const body = opts.getResponseData(xhr.responseText, xhr);
 | 
						||
        const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr));
 | 
						||
        const response = {
 | 
						||
          status: xhr.status,
 | 
						||
          body
 | 
						||
        };
 | 
						||
        this.uppy.emit('upload-error', file, error, response);
 | 
						||
        return reject(error);
 | 
						||
      });
 | 
						||
      xhr.addEventListener('error', () => {
 | 
						||
        this.uppy.log(`[XHRUpload] ${id} errored`);
 | 
						||
        timer.done();
 | 
						||
        queuedRequest.done();
 | 
						||
 | 
						||
        if (this.uploaderEvents[file.id]) {
 | 
						||
          this.uploaderEvents[file.id].remove();
 | 
						||
          this.uploaderEvents[file.id] = null;
 | 
						||
        }
 | 
						||
 | 
						||
        const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr));
 | 
						||
        this.uppy.emit('upload-error', file, error);
 | 
						||
        return reject(error);
 | 
						||
      });
 | 
						||
      xhr.open(opts.method.toUpperCase(), opts.endpoint, true); // IE10 does not allow setting `withCredentials` and `responseType`
 | 
						||
      // before `open()` is called.
 | 
						||
 | 
						||
      xhr.withCredentials = opts.withCredentials;
 | 
						||
 | 
						||
      if (opts.responseType !== '') {
 | 
						||
        xhr.responseType = opts.responseType;
 | 
						||
      }
 | 
						||
 | 
						||
      queuedRequest = this.requests.run(() => {
 | 
						||
        this.uppy.emit('upload-started', file); // When using an authentication system like JWT, the bearer token goes as a header. This
 | 
						||
        // header needs to be fresh each time the token is refreshed so computing and setting the
 | 
						||
        // headers just before the upload starts enables this kind of authentication to work properly.
 | 
						||
        // Otherwise, half-way through the list of uploads the token could be stale and the upload would fail.
 | 
						||
 | 
						||
        const currentOpts = this.getOptions(file);
 | 
						||
        Object.keys(currentOpts.headers).forEach(header => {
 | 
						||
          xhr.setRequestHeader(header, currentOpts.headers[header]);
 | 
						||
        });
 | 
						||
        xhr.send(data);
 | 
						||
        return () => {
 | 
						||
          timer.done();
 | 
						||
          xhr.abort();
 | 
						||
        };
 | 
						||
      });
 | 
						||
      this.onFileRemove(file.id, () => {
 | 
						||
        queuedRequest.abort();
 | 
						||
        reject(new Error('File removed'));
 | 
						||
      });
 | 
						||
      this.onCancelAll(file.id, _ref => {
 | 
						||
        let {
 | 
						||
          reason
 | 
						||
        } = _ref;
 | 
						||
 | 
						||
        if (reason === 'user') {
 | 
						||
          queuedRequest.abort();
 | 
						||
        }
 | 
						||
 | 
						||
        reject(new Error('Upload cancelled'));
 | 
						||
      });
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  uploadRemote(file) {
 | 
						||
    const opts = this.getOptions(file);
 | 
						||
    return new Promise((resolve, reject) => {
 | 
						||
      this.uppy.emit('upload-started', file);
 | 
						||
      const fields = {};
 | 
						||
      const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default.
 | 
						||
      : Object.keys(file.meta);
 | 
						||
      metaFields.forEach(name => {
 | 
						||
        fields[name] = file.meta[name];
 | 
						||
      });
 | 
						||
      const Client = file.remote.providerOptions.provider ? _companionClient.Provider : _companionClient.RequestClient;
 | 
						||
      const client = new Client(this.uppy, file.remote.providerOptions);
 | 
						||
      client.post(file.remote.url, { ...file.remote.body,
 | 
						||
        endpoint: opts.endpoint,
 | 
						||
        size: file.data.size,
 | 
						||
        fieldname: opts.fieldName,
 | 
						||
        metadata: fields,
 | 
						||
        httpMethod: opts.method,
 | 
						||
        useFormData: opts.formData,
 | 
						||
        headers: opts.headers
 | 
						||
      }).then(res => {
 | 
						||
        const {
 | 
						||
          token
 | 
						||
        } = res;
 | 
						||
        const host = getSocketHost(file.remote.companionUrl);
 | 
						||
        const socket = new _companionClient.Socket({
 | 
						||
          target: `${host}/api/${token}`,
 | 
						||
          autoOpen: false
 | 
						||
        });
 | 
						||
        this.uploaderEvents[file.id] = new EventTracker(this.uppy);
 | 
						||
        let queuedRequest;
 | 
						||
        this.onFileRemove(file.id, () => {
 | 
						||
          socket.send('cancel', {});
 | 
						||
          queuedRequest.abort();
 | 
						||
          resolve(`upload ${file.id} was removed`);
 | 
						||
        });
 | 
						||
        this.onCancelAll(file.id, function (_temp) {
 | 
						||
          let {
 | 
						||
            reason
 | 
						||
          } = _temp === void 0 ? {} : _temp;
 | 
						||
 | 
						||
          if (reason === 'user') {
 | 
						||
            socket.send('cancel', {});
 | 
						||
            queuedRequest.abort();
 | 
						||
          }
 | 
						||
 | 
						||
          resolve(`upload ${file.id} was canceled`);
 | 
						||
        });
 | 
						||
        this.onRetry(file.id, () => {
 | 
						||
          socket.send('pause', {});
 | 
						||
          socket.send('resume', {});
 | 
						||
        });
 | 
						||
        this.onRetryAll(file.id, () => {
 | 
						||
          socket.send('pause', {});
 | 
						||
          socket.send('resume', {});
 | 
						||
        });
 | 
						||
        socket.on('progress', progressData => emitSocketProgress(this, progressData, file));
 | 
						||
        socket.on('success', data => {
 | 
						||
          const body = opts.getResponseData(data.response.responseText, data.response);
 | 
						||
          const uploadURL = body[opts.responseUrlFieldName];
 | 
						||
          const uploadResp = {
 | 
						||
            status: data.response.status,
 | 
						||
            body,
 | 
						||
            uploadURL
 | 
						||
          };
 | 
						||
          this.uppy.emit('upload-success', file, uploadResp);
 | 
						||
          queuedRequest.done();
 | 
						||
 | 
						||
          if (this.uploaderEvents[file.id]) {
 | 
						||
            this.uploaderEvents[file.id].remove();
 | 
						||
            this.uploaderEvents[file.id] = null;
 | 
						||
          }
 | 
						||
 | 
						||
          return resolve();
 | 
						||
        });
 | 
						||
        socket.on('error', errData => {
 | 
						||
          const resp = errData.response;
 | 
						||
          const error = resp ? opts.getResponseError(resp.responseText, resp) : Object.assign(new Error(errData.error.message), {
 | 
						||
            cause: errData.error
 | 
						||
          });
 | 
						||
          this.uppy.emit('upload-error', file, error);
 | 
						||
          queuedRequest.done();
 | 
						||
 | 
						||
          if (this.uploaderEvents[file.id]) {
 | 
						||
            this.uploaderEvents[file.id].remove();
 | 
						||
            this.uploaderEvents[file.id] = null;
 | 
						||
          }
 | 
						||
 | 
						||
          reject(error);
 | 
						||
        });
 | 
						||
        queuedRequest = this.requests.run(() => {
 | 
						||
          socket.open();
 | 
						||
 | 
						||
          if (file.isPaused) {
 | 
						||
            socket.send('pause', {});
 | 
						||
          }
 | 
						||
 | 
						||
          return () => socket.close();
 | 
						||
        });
 | 
						||
      }).catch(err => {
 | 
						||
        this.uppy.emit('upload-error', file, err);
 | 
						||
        reject(err);
 | 
						||
      });
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  uploadBundle(files) {
 | 
						||
    return new Promise((resolve, reject) => {
 | 
						||
      const {
 | 
						||
        endpoint
 | 
						||
      } = this.opts;
 | 
						||
      const {
 | 
						||
        method
 | 
						||
      } = this.opts;
 | 
						||
      const optsFromState = this.uppy.getState().xhrUpload;
 | 
						||
      const formData = this.createBundledUpload(files, { ...this.opts,
 | 
						||
        ...(optsFromState || {})
 | 
						||
      });
 | 
						||
      const xhr = new XMLHttpRequest();
 | 
						||
 | 
						||
      const emitError = error => {
 | 
						||
        files.forEach(file => {
 | 
						||
          this.uppy.emit('upload-error', file, error);
 | 
						||
        });
 | 
						||
      };
 | 
						||
 | 
						||
      const timer = new ProgressTimeout(this.opts.timeout, () => {
 | 
						||
        xhr.abort();
 | 
						||
        const error = new Error(this.i18n('timedOut', {
 | 
						||
          seconds: Math.ceil(this.opts.timeout / 1000)
 | 
						||
        }));
 | 
						||
        emitError(error);
 | 
						||
        reject(error);
 | 
						||
      });
 | 
						||
      xhr.upload.addEventListener('loadstart', () => {
 | 
						||
        this.uppy.log('[XHRUpload] started uploading bundle');
 | 
						||
        timer.progress();
 | 
						||
      });
 | 
						||
      xhr.upload.addEventListener('progress', ev => {
 | 
						||
        timer.progress();
 | 
						||
        if (!ev.lengthComputable) return;
 | 
						||
        files.forEach(file => {
 | 
						||
          this.uppy.emit('upload-progress', file, {
 | 
						||
            uploader: this,
 | 
						||
            bytesUploaded: ev.loaded / ev.total * file.size,
 | 
						||
            bytesTotal: file.size
 | 
						||
          });
 | 
						||
        });
 | 
						||
      });
 | 
						||
      xhr.addEventListener('load', ev => {
 | 
						||
        timer.done();
 | 
						||
 | 
						||
        if (this.opts.validateStatus(ev.target.status, xhr.responseText, xhr)) {
 | 
						||
          const body = this.opts.getResponseData(xhr.responseText, xhr);
 | 
						||
          const uploadResp = {
 | 
						||
            status: ev.target.status,
 | 
						||
            body
 | 
						||
          };
 | 
						||
          files.forEach(file => {
 | 
						||
            this.uppy.emit('upload-success', file, uploadResp);
 | 
						||
          });
 | 
						||
          return resolve();
 | 
						||
        }
 | 
						||
 | 
						||
        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error');
 | 
						||
        error.request = xhr;
 | 
						||
        emitError(error);
 | 
						||
        return reject(error);
 | 
						||
      });
 | 
						||
      xhr.addEventListener('error', () => {
 | 
						||
        timer.done();
 | 
						||
        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error');
 | 
						||
        emitError(error);
 | 
						||
        return reject(error);
 | 
						||
      });
 | 
						||
      this.uppy.on('cancel-all', function (_temp2) {
 | 
						||
        let {
 | 
						||
          reason
 | 
						||
        } = _temp2 === void 0 ? {} : _temp2;
 | 
						||
        if (reason !== 'user') return;
 | 
						||
        timer.done();
 | 
						||
        xhr.abort();
 | 
						||
      });
 | 
						||
      xhr.open(method.toUpperCase(), endpoint, true); // IE10 does not allow setting `withCredentials` and `responseType`
 | 
						||
      // before `open()` is called.
 | 
						||
 | 
						||
      xhr.withCredentials = this.opts.withCredentials;
 | 
						||
 | 
						||
      if (this.opts.responseType !== '') {
 | 
						||
        xhr.responseType = this.opts.responseType;
 | 
						||
      }
 | 
						||
 | 
						||
      Object.keys(this.opts.headers).forEach(header => {
 | 
						||
        xhr.setRequestHeader(header, this.opts.headers[header]);
 | 
						||
      });
 | 
						||
      xhr.send(formData);
 | 
						||
      files.forEach(file => {
 | 
						||
        this.uppy.emit('upload-started', file);
 | 
						||
      });
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  uploadFiles(files) {
 | 
						||
    const promises = files.map((file, i) => {
 | 
						||
      const current = parseInt(i, 10) + 1;
 | 
						||
      const total = files.length;
 | 
						||
 | 
						||
      if (file.error) {
 | 
						||
        return Promise.reject(new Error(file.error));
 | 
						||
      }
 | 
						||
 | 
						||
      if (file.isRemote) {
 | 
						||
        return this.uploadRemote(file, current, total);
 | 
						||
      }
 | 
						||
 | 
						||
      return this.upload(file, current, total);
 | 
						||
    });
 | 
						||
    return settle(promises);
 | 
						||
  }
 | 
						||
 | 
						||
  onFileRemove(fileID, cb) {
 | 
						||
    this.uploaderEvents[fileID].on('file-removed', file => {
 | 
						||
      if (fileID === file.id) cb(file.id);
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  onRetry(fileID, cb) {
 | 
						||
    this.uploaderEvents[fileID].on('upload-retry', targetFileID => {
 | 
						||
      if (fileID === targetFileID) {
 | 
						||
        cb();
 | 
						||
      }
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  onRetryAll(fileID, cb) {
 | 
						||
    this.uploaderEvents[fileID].on('retry-all', () => {
 | 
						||
      if (!this.uppy.getFile(fileID)) return;
 | 
						||
      cb();
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  onCancelAll(fileID, eventHandler) {
 | 
						||
    var _this = this;
 | 
						||
 | 
						||
    this.uploaderEvents[fileID].on('cancel-all', function () {
 | 
						||
      if (!_this.uppy.getFile(fileID)) return;
 | 
						||
      eventHandler(...arguments);
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  handleUpload(fileIDs) {
 | 
						||
    if (fileIDs.length === 0) {
 | 
						||
      this.uppy.log('[XHRUpload] No files to upload!');
 | 
						||
      return Promise.resolve();
 | 
						||
    } // No limit configured by the user, and no RateLimitedQueue passed in by a "parent" plugin
 | 
						||
    // (basically just AwsS3) using the internal symbol
 | 
						||
 | 
						||
 | 
						||
    if (this.opts.limit === 0 && !this.opts[_RateLimitedQueue.internalRateLimitedQueue]) {
 | 
						||
      this.uppy.log('[XHRUpload] When uploading multiple files at once, consider setting the `limit` option (to `10` for example), to limit the number of concurrent uploads, which helps prevent memory and network issues: https://uppy.io/docs/xhr-upload/#limit-0', 'warning');
 | 
						||
    }
 | 
						||
 | 
						||
    this.uppy.log('[XHRUpload] Uploading...');
 | 
						||
    const files = fileIDs.map(fileID => this.uppy.getFile(fileID));
 | 
						||
 | 
						||
    if (this.opts.bundle) {
 | 
						||
      // if bundle: true, we don’t support remote uploads
 | 
						||
      const isSomeFileRemote = files.some(file => file.isRemote);
 | 
						||
 | 
						||
      if (isSomeFileRemote) {
 | 
						||
        throw new Error('Can’t upload remote files when the `bundle: true` option is set');
 | 
						||
      }
 | 
						||
 | 
						||
      if (typeof this.opts.headers === 'function') {
 | 
						||
        throw new TypeError('`headers` may not be a function when the `bundle: true` option is set');
 | 
						||
      }
 | 
						||
 | 
						||
      return this.uploadBundle(files);
 | 
						||
    }
 | 
						||
 | 
						||
    return this.uploadFiles(files).then(() => null);
 | 
						||
  }
 | 
						||
 | 
						||
  install() {
 | 
						||
    if (this.opts.bundle) {
 | 
						||
      const {
 | 
						||
        capabilities
 | 
						||
      } = this.uppy.getState();
 | 
						||
      this.uppy.setState({
 | 
						||
        capabilities: { ...capabilities,
 | 
						||
          individualCancellation: false
 | 
						||
        }
 | 
						||
      });
 | 
						||
    }
 | 
						||
 | 
						||
    this.uppy.addUploader(this.handleUpload);
 | 
						||
  }
 | 
						||
 | 
						||
  uninstall() {
 | 
						||
    if (this.opts.bundle) {
 | 
						||
      const {
 | 
						||
        capabilities
 | 
						||
      } = this.uppy.getState();
 | 
						||
      this.uppy.setState({
 | 
						||
        capabilities: { ...capabilities,
 | 
						||
          individualCancellation: true
 | 
						||
        }
 | 
						||
      });
 | 
						||
    }
 | 
						||
 | 
						||
    this.uppy.removeUploader(this.handleUpload);
 | 
						||
  }
 | 
						||
 | 
						||
}
 | 
						||
 | 
						||
XHRUpload.VERSION = packageJson.version;
 | 
						||
module.exports = XHRUpload; |