7617 lines
237 KiB
JavaScript
7617 lines
237 KiB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||
// Adapted from https://github.com/Flet/prettier-bytes/
|
||
// Changing 1000 bytes to 1024, so we can keep uppercase KB vs kB
|
||
// ISC License (c) Dan Flettre https://github.com/Flet/prettier-bytes/blob/master/LICENSE
|
||
module.exports = function prettierBytes (num) {
|
||
if (typeof num !== 'number' || isNaN(num)) {
|
||
throw new TypeError('Expected a number, got ' + typeof num)
|
||
}
|
||
|
||
var neg = num < 0
|
||
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||
|
||
if (neg) {
|
||
num = -num
|
||
}
|
||
|
||
if (num < 1) {
|
||
return (neg ? '-' : '') + num + ' B'
|
||
}
|
||
|
||
var exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1)
|
||
num = Number(num / Math.pow(1024, exponent))
|
||
var unit = units[exponent]
|
||
|
||
if (num >= 10 || num % 1 === 0) {
|
||
// Do not show decimals when the number is two-digit, or if the number has no
|
||
// decimal component.
|
||
return (neg ? '-' : '') + num.toFixed(0) + ' ' + unit
|
||
} else {
|
||
return (neg ? '-' : '') + num.toFixed(1) + ' ' + unit
|
||
}
|
||
}
|
||
|
||
},{}],2:[function(require,module,exports){
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const {
|
||
AbortController,
|
||
createAbortError
|
||
} = require('@uppy/utils/lib/AbortController');
|
||
|
||
const delay = require('@uppy/utils/lib/delay');
|
||
|
||
const MB = 1024 * 1024;
|
||
const defaultOptions = {
|
||
limit: 1,
|
||
retryDelays: [0, 1000, 3000, 5000],
|
||
|
||
getChunkSize(file) {
|
||
return Math.ceil(file.size / 10000);
|
||
},
|
||
|
||
onStart() {},
|
||
|
||
onProgress() {},
|
||
|
||
onPartComplete() {},
|
||
|
||
onSuccess() {},
|
||
|
||
onError(err) {
|
||
throw err;
|
||
}
|
||
|
||
};
|
||
|
||
function ensureInt(value) {
|
||
if (typeof value === 'string') {
|
||
return parseInt(value, 10);
|
||
}
|
||
|
||
if (typeof value === 'number') {
|
||
return value;
|
||
}
|
||
|
||
throw new TypeError('Expected a number');
|
||
}
|
||
|
||
var _aborted = /*#__PURE__*/_classPrivateFieldLooseKey("aborted");
|
||
|
||
var _initChunks = /*#__PURE__*/_classPrivateFieldLooseKey("initChunks");
|
||
|
||
var _createUpload = /*#__PURE__*/_classPrivateFieldLooseKey("createUpload");
|
||
|
||
var _resumeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("resumeUpload");
|
||
|
||
var _uploadParts = /*#__PURE__*/_classPrivateFieldLooseKey("uploadParts");
|
||
|
||
var _retryable = /*#__PURE__*/_classPrivateFieldLooseKey("retryable");
|
||
|
||
var _prepareUploadParts = /*#__PURE__*/_classPrivateFieldLooseKey("prepareUploadParts");
|
||
|
||
var _uploadPartRetryable = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPartRetryable");
|
||
|
||
var _uploadPart = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPart");
|
||
|
||
var _onPartProgress = /*#__PURE__*/_classPrivateFieldLooseKey("onPartProgress");
|
||
|
||
var _onPartComplete = /*#__PURE__*/_classPrivateFieldLooseKey("onPartComplete");
|
||
|
||
var _uploadPartBytes = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPartBytes");
|
||
|
||
var _completeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("completeUpload");
|
||
|
||
var _abortUpload = /*#__PURE__*/_classPrivateFieldLooseKey("abortUpload");
|
||
|
||
var _onError = /*#__PURE__*/_classPrivateFieldLooseKey("onError");
|
||
|
||
class MultipartUploader {
|
||
constructor(file, options) {
|
||
Object.defineProperty(this, _onError, {
|
||
value: _onError2
|
||
});
|
||
Object.defineProperty(this, _abortUpload, {
|
||
value: _abortUpload2
|
||
});
|
||
Object.defineProperty(this, _completeUpload, {
|
||
value: _completeUpload2
|
||
});
|
||
Object.defineProperty(this, _uploadPartBytes, {
|
||
value: _uploadPartBytes2
|
||
});
|
||
Object.defineProperty(this, _onPartComplete, {
|
||
value: _onPartComplete2
|
||
});
|
||
Object.defineProperty(this, _onPartProgress, {
|
||
value: _onPartProgress2
|
||
});
|
||
Object.defineProperty(this, _uploadPart, {
|
||
value: _uploadPart2
|
||
});
|
||
Object.defineProperty(this, _uploadPartRetryable, {
|
||
value: _uploadPartRetryable2
|
||
});
|
||
Object.defineProperty(this, _prepareUploadParts, {
|
||
value: _prepareUploadParts2
|
||
});
|
||
Object.defineProperty(this, _retryable, {
|
||
value: _retryable2
|
||
});
|
||
Object.defineProperty(this, _uploadParts, {
|
||
value: _uploadParts2
|
||
});
|
||
Object.defineProperty(this, _resumeUpload, {
|
||
value: _resumeUpload2
|
||
});
|
||
Object.defineProperty(this, _createUpload, {
|
||
value: _createUpload2
|
||
});
|
||
Object.defineProperty(this, _initChunks, {
|
||
value: _initChunks2
|
||
});
|
||
Object.defineProperty(this, _aborted, {
|
||
value: _aborted2
|
||
});
|
||
this.options = { ...defaultOptions,
|
||
...options
|
||
}; // Use default `getChunkSize` if it was null or something
|
||
|
||
if (!this.options.getChunkSize) {
|
||
this.options.getChunkSize = defaultOptions.getChunkSize;
|
||
}
|
||
|
||
this.file = file;
|
||
this.abortController = new AbortController();
|
||
this.key = this.options.key || null;
|
||
this.uploadId = this.options.uploadId || null;
|
||
this.parts = []; // Do `this.createdPromise.then(OP)` to execute an operation `OP` _only_ if the
|
||
// upload was created already. That also ensures that the sequencing is right
|
||
// (so the `OP` definitely happens if the upload is created).
|
||
//
|
||
// This mostly exists to make `#abortUpload` work well: only sending the abort request if
|
||
// the upload was already created, and if the createMultipartUpload request is still in flight,
|
||
// aborting it immediately after it finishes.
|
||
|
||
this.createdPromise = Promise.reject(); // eslint-disable-line prefer-promise-reject-errors
|
||
|
||
this.isPaused = false;
|
||
this.partsInProgress = 0;
|
||
this.chunks = null;
|
||
this.chunkState = null;
|
||
|
||
_classPrivateFieldLooseBase(this, _initChunks)[_initChunks]();
|
||
|
||
this.createdPromise.catch(() => {}); // silence uncaught rejection warning
|
||
}
|
||
/**
|
||
* Was this upload aborted?
|
||
*
|
||
* If yes, we may need to throw an AbortError.
|
||
*
|
||
* @returns {boolean}
|
||
*/
|
||
|
||
|
||
start() {
|
||
this.isPaused = false;
|
||
|
||
if (this.uploadId) {
|
||
_classPrivateFieldLooseBase(this, _resumeUpload)[_resumeUpload]();
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _createUpload)[_createUpload]();
|
||
}
|
||
}
|
||
|
||
pause() {
|
||
this.abortController.abort(); // Swap it out for a new controller, because this instance may be resumed later.
|
||
|
||
this.abortController = new AbortController();
|
||
this.isPaused = true;
|
||
}
|
||
|
||
abort(opts) {
|
||
var _opts;
|
||
|
||
if (opts === void 0) {
|
||
opts = undefined;
|
||
}
|
||
|
||
if ((_opts = opts) != null && _opts.really) _classPrivateFieldLooseBase(this, _abortUpload)[_abortUpload]();else this.pause();
|
||
}
|
||
|
||
}
|
||
|
||
function _aborted2() {
|
||
return this.abortController.signal.aborted;
|
||
}
|
||
|
||
function _initChunks2() {
|
||
const chunks = [];
|
||
const desiredChunkSize = this.options.getChunkSize(this.file); // at least 5MB per request, at most 10k requests
|
||
|
||
const minChunkSize = Math.max(5 * MB, Math.ceil(this.file.size / 10000));
|
||
const chunkSize = Math.max(desiredChunkSize, minChunkSize); // Upload zero-sized files in one zero-sized chunk
|
||
|
||
if (this.file.size === 0) {
|
||
chunks.push(this.file);
|
||
} else {
|
||
for (let i = 0; i < this.file.size; i += chunkSize) {
|
||
const end = Math.min(this.file.size, i + chunkSize);
|
||
chunks.push(this.file.slice(i, end));
|
||
}
|
||
}
|
||
|
||
this.chunks = chunks;
|
||
this.chunkState = chunks.map(() => ({
|
||
uploaded: 0,
|
||
busy: false,
|
||
done: false
|
||
}));
|
||
}
|
||
|
||
function _createUpload2() {
|
||
this.createdPromise = Promise.resolve().then(() => this.options.createMultipartUpload());
|
||
return this.createdPromise.then(result => {
|
||
if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError();
|
||
const valid = typeof result === 'object' && result && typeof result.uploadId === 'string' && typeof result.key === 'string';
|
||
|
||
if (!valid) {
|
||
throw new TypeError('AwsS3/Multipart: Got incorrect result from `createMultipartUpload()`, expected an object `{ uploadId, key }`.');
|
||
}
|
||
|
||
this.key = result.key;
|
||
this.uploadId = result.uploadId;
|
||
this.options.onStart(result);
|
||
|
||
_classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts]();
|
||
}).catch(err => {
|
||
_classPrivateFieldLooseBase(this, _onError)[_onError](err);
|
||
});
|
||
}
|
||
|
||
async function _resumeUpload2() {
|
||
try {
|
||
const parts = await this.options.listParts({
|
||
uploadId: this.uploadId,
|
||
key: this.key
|
||
});
|
||
if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError();
|
||
parts.forEach(part => {
|
||
const i = part.PartNumber - 1;
|
||
this.chunkState[i] = {
|
||
uploaded: ensureInt(part.Size),
|
||
etag: part.ETag,
|
||
done: true
|
||
}; // Only add if we did not yet know about this part.
|
||
|
||
if (!this.parts.some(p => p.PartNumber === part.PartNumber)) {
|
||
this.parts.push({
|
||
PartNumber: part.PartNumber,
|
||
ETag: part.ETag
|
||
});
|
||
}
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts]();
|
||
} catch (err) {
|
||
_classPrivateFieldLooseBase(this, _onError)[_onError](err);
|
||
}
|
||
}
|
||
|
||
function _uploadParts2() {
|
||
if (this.isPaused) return; // All parts are uploaded.
|
||
|
||
if (this.chunkState.every(state => state.done)) {
|
||
_classPrivateFieldLooseBase(this, _completeUpload)[_completeUpload]();
|
||
|
||
return;
|
||
} // For a 100MB file, with the default min chunk size of 5MB and a limit of 10:
|
||
//
|
||
// Total 20 parts
|
||
// ---------
|
||
// Need 1 is 10
|
||
// Need 2 is 5
|
||
// Need 3 is 5
|
||
|
||
|
||
const need = this.options.limit - this.partsInProgress;
|
||
const completeChunks = this.chunkState.filter(state => state.done).length;
|
||
const remainingChunks = this.chunks.length - completeChunks;
|
||
let minNeeded = Math.ceil(this.options.limit / 2);
|
||
|
||
if (minNeeded > remainingChunks) {
|
||
minNeeded = remainingChunks;
|
||
}
|
||
|
||
if (need < minNeeded) return;
|
||
const candidates = [];
|
||
|
||
for (let i = 0; i < this.chunkState.length; i++) {
|
||
const state = this.chunkState[i]; // eslint-disable-next-line no-continue
|
||
|
||
if (state.done || state.busy) continue;
|
||
candidates.push(i);
|
||
|
||
if (candidates.length >= need) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (candidates.length === 0) return;
|
||
|
||
_classPrivateFieldLooseBase(this, _prepareUploadParts)[_prepareUploadParts](candidates).then(result => {
|
||
candidates.forEach(index => {
|
||
const partNumber = index + 1;
|
||
const prePreparedPart = {
|
||
url: result.presignedUrls[partNumber],
|
||
headers: result.headers
|
||
};
|
||
|
||
_classPrivateFieldLooseBase(this, _uploadPartRetryable)[_uploadPartRetryable](index, prePreparedPart).then(() => {
|
||
_classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts]();
|
||
}, err => {
|
||
_classPrivateFieldLooseBase(this, _onError)[_onError](err);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
function _retryable2(_ref) {
|
||
let {
|
||
before,
|
||
attempt,
|
||
after
|
||
} = _ref;
|
||
const {
|
||
retryDelays
|
||
} = this.options;
|
||
const {
|
||
signal
|
||
} = this.abortController;
|
||
if (before) before();
|
||
|
||
function shouldRetry(err) {
|
||
if (err.source && typeof err.source.status === 'number') {
|
||
const {
|
||
status
|
||
} = err.source; // 0 probably indicates network failure
|
||
|
||
return status === 0 || status === 409 || status === 423 || status >= 500 && status < 600;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
const doAttempt = retryAttempt => attempt().catch(err => {
|
||
if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError();
|
||
|
||
if (shouldRetry(err) && retryAttempt < retryDelays.length) {
|
||
return delay(retryDelays[retryAttempt], {
|
||
signal
|
||
}).then(() => doAttempt(retryAttempt + 1));
|
||
}
|
||
|
||
throw err;
|
||
});
|
||
|
||
return doAttempt(0).then(result => {
|
||
if (after) after();
|
||
return result;
|
||
}, err => {
|
||
if (after) after();
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
async function _prepareUploadParts2(candidates) {
|
||
candidates.forEach(i => {
|
||
this.chunkState[i].busy = true;
|
||
});
|
||
const result = await _classPrivateFieldLooseBase(this, _retryable)[_retryable]({
|
||
attempt: () => this.options.prepareUploadParts({
|
||
key: this.key,
|
||
uploadId: this.uploadId,
|
||
partNumbers: candidates.map(index => index + 1),
|
||
chunks: candidates.reduce((chunks, candidate) => ({ ...chunks,
|
||
// Use the part number as the index
|
||
[candidate + 1]: this.chunks[candidate]
|
||
}), {})
|
||
})
|
||
});
|
||
|
||
if (typeof (result == null ? void 0 : result.presignedUrls) !== 'object') {
|
||
throw new TypeError('AwsS3/Multipart: Got incorrect result from `prepareUploadParts()`, expected an object `{ presignedUrls }`.');
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
function _uploadPartRetryable2(index, prePreparedPart) {
|
||
return _classPrivateFieldLooseBase(this, _retryable)[_retryable]({
|
||
before: () => {
|
||
this.partsInProgress += 1;
|
||
},
|
||
attempt: () => _classPrivateFieldLooseBase(this, _uploadPart)[_uploadPart](index, prePreparedPart),
|
||
after: () => {
|
||
this.partsInProgress -= 1;
|
||
}
|
||
});
|
||
}
|
||
|
||
function _uploadPart2(index, prePreparedPart) {
|
||
this.chunkState[index].busy = true;
|
||
const valid = typeof (prePreparedPart == null ? void 0 : prePreparedPart.url) === 'string';
|
||
|
||
if (!valid) {
|
||
throw new TypeError('AwsS3/Multipart: Got incorrect result for `prePreparedPart`, expected an object `{ url }`.');
|
||
}
|
||
|
||
const {
|
||
url,
|
||
headers
|
||
} = prePreparedPart;
|
||
|
||
if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) {
|
||
this.chunkState[index].busy = false;
|
||
throw createAbortError();
|
||
}
|
||
|
||
return _classPrivateFieldLooseBase(this, _uploadPartBytes)[_uploadPartBytes](index, url, headers);
|
||
}
|
||
|
||
function _onPartProgress2(index, sent) {
|
||
this.chunkState[index].uploaded = ensureInt(sent);
|
||
const totalUploaded = this.chunkState.reduce((n, c) => n + c.uploaded, 0);
|
||
this.options.onProgress(totalUploaded, this.file.size);
|
||
}
|
||
|
||
function _onPartComplete2(index, etag) {
|
||
this.chunkState[index].etag = etag;
|
||
this.chunkState[index].done = true;
|
||
const part = {
|
||
PartNumber: index + 1,
|
||
ETag: etag
|
||
};
|
||
this.parts.push(part);
|
||
this.options.onPartComplete(part);
|
||
}
|
||
|
||
function _uploadPartBytes2(index, url, headers) {
|
||
const body = this.chunks[index];
|
||
const {
|
||
signal
|
||
} = this.abortController;
|
||
let defer;
|
||
const promise = new Promise((resolve, reject) => {
|
||
defer = {
|
||
resolve,
|
||
reject
|
||
};
|
||
});
|
||
const xhr = new XMLHttpRequest();
|
||
xhr.open('PUT', url, true);
|
||
|
||
if (headers) {
|
||
Object.keys(headers).forEach(key => {
|
||
xhr.setRequestHeader(key, headers[key]);
|
||
});
|
||
}
|
||
|
||
xhr.responseType = 'text';
|
||
|
||
function cleanup() {
|
||
// eslint-disable-next-line no-use-before-define
|
||
signal.removeEventListener('abort', onabort);
|
||
}
|
||
|
||
function onabort() {
|
||
xhr.abort();
|
||
}
|
||
|
||
signal.addEventListener('abort', onabort);
|
||
xhr.upload.addEventListener('progress', ev => {
|
||
if (!ev.lengthComputable) return;
|
||
|
||
_classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](index, ev.loaded, ev.total);
|
||
});
|
||
xhr.addEventListener('abort', () => {
|
||
cleanup();
|
||
this.chunkState[index].busy = false;
|
||
defer.reject(createAbortError());
|
||
});
|
||
xhr.addEventListener('load', ev => {
|
||
cleanup();
|
||
this.chunkState[index].busy = false;
|
||
|
||
if (ev.target.status < 200 || ev.target.status >= 300) {
|
||
const error = new Error('Non 2xx');
|
||
error.source = ev.target;
|
||
defer.reject(error);
|
||
return;
|
||
} // This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers.
|
||
|
||
|
||
this.chunks[index] = null;
|
||
|
||
_classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](index, body.size, body.size); // NOTE This must be allowed by CORS.
|
||
|
||
|
||
const etag = ev.target.getResponseHeader('ETag');
|
||
|
||
if (etag === null) {
|
||
defer.reject(new Error('AwsS3/Multipart: Could not read the ETag header. This likely means CORS is not configured correctly on the S3 Bucket. See https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.'));
|
||
return;
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _onPartComplete)[_onPartComplete](index, etag);
|
||
|
||
defer.resolve();
|
||
});
|
||
xhr.addEventListener('error', ev => {
|
||
cleanup();
|
||
this.chunkState[index].busy = false;
|
||
const error = new Error('Unknown error');
|
||
error.source = ev.target;
|
||
defer.reject(error);
|
||
});
|
||
xhr.send(body);
|
||
return promise;
|
||
}
|
||
|
||
async function _completeUpload2() {
|
||
// Parts may not have completed uploading in sorted order, if limit > 1.
|
||
this.parts.sort((a, b) => a.PartNumber - b.PartNumber);
|
||
|
||
try {
|
||
const result = await this.options.completeMultipartUpload({
|
||
key: this.key,
|
||
uploadId: this.uploadId,
|
||
parts: this.parts
|
||
});
|
||
this.options.onSuccess(result);
|
||
} catch (err) {
|
||
_classPrivateFieldLooseBase(this, _onError)[_onError](err);
|
||
}
|
||
}
|
||
|
||
function _abortUpload2() {
|
||
this.abortController.abort();
|
||
this.createdPromise.then(() => {
|
||
this.options.abortMultipartUpload({
|
||
key: this.key,
|
||
uploadId: this.uploadId
|
||
});
|
||
}, () => {// if the creation failed we do not need to abort
|
||
});
|
||
}
|
||
|
||
function _onError2(err) {
|
||
if (err && err.name === 'AbortError') {
|
||
return;
|
||
}
|
||
|
||
this.options.onError(err);
|
||
}
|
||
|
||
module.exports = MultipartUploader;
|
||
},{"@uppy/utils/lib/AbortController":26,"@uppy/utils/lib/delay":32}],3:[function(require,module,exports){
|
||
var _class, _temp;
|
||
|
||
const BasePlugin = require('@uppy/core/lib/BasePlugin');
|
||
|
||
const {
|
||
Socket,
|
||
Provider,
|
||
RequestClient
|
||
} = require('@uppy/companion-client');
|
||
|
||
const EventTracker = require('@uppy/utils/lib/EventTracker');
|
||
|
||
const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress');
|
||
|
||
const getSocketHost = require('@uppy/utils/lib/getSocketHost');
|
||
|
||
const {
|
||
RateLimitedQueue
|
||
} = require('@uppy/utils/lib/RateLimitedQueue');
|
||
|
||
const MultipartUploader = require('./MultipartUploader');
|
||
|
||
function assertServerError(res) {
|
||
if (res && res.error) {
|
||
const error = new Error(res.message);
|
||
Object.assign(error, res.error);
|
||
throw error;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
module.exports = (_temp = _class = class AwsS3Multipart extends BasePlugin {
|
||
constructor(uppy, opts) {
|
||
super(uppy, opts);
|
||
this.type = 'uploader';
|
||
this.id = this.opts.id || 'AwsS3Multipart';
|
||
this.title = 'AWS S3 Multipart';
|
||
this.client = new RequestClient(uppy, opts);
|
||
const defaultOptions = {
|
||
timeout: 30 * 1000,
|
||
limit: 0,
|
||
retryDelays: [0, 1000, 3000, 5000],
|
||
createMultipartUpload: this.createMultipartUpload.bind(this),
|
||
listParts: this.listParts.bind(this),
|
||
prepareUploadParts: this.prepareUploadParts.bind(this),
|
||
abortMultipartUpload: this.abortMultipartUpload.bind(this),
|
||
completeMultipartUpload: this.completeMultipartUpload.bind(this)
|
||
};
|
||
this.opts = { ...defaultOptions,
|
||
...opts
|
||
};
|
||
this.upload = this.upload.bind(this);
|
||
this.requests = new RateLimitedQueue(this.opts.limit);
|
||
this.uploaders = Object.create(null);
|
||
this.uploaderEvents = Object.create(null);
|
||
this.uploaderSockets = Object.create(null);
|
||
}
|
||
/**
|
||
* Clean up all references for a file's upload: the MultipartUploader instance,
|
||
* any events related to the file, and the Companion WebSocket connection.
|
||
*
|
||
* Set `opts.abort` to tell S3 that the multipart upload is cancelled and must be removed.
|
||
* This should be done when the user cancels the upload, not when the upload is completed or errored.
|
||
*/
|
||
|
||
|
||
resetUploaderReferences(fileID, opts) {
|
||
if (opts === void 0) {
|
||
opts = {};
|
||
}
|
||
|
||
if (this.uploaders[fileID]) {
|
||
this.uploaders[fileID].abort({
|
||
really: opts.abort || false
|
||
});
|
||
this.uploaders[fileID] = null;
|
||
}
|
||
|
||
if (this.uploaderEvents[fileID]) {
|
||
this.uploaderEvents[fileID].remove();
|
||
this.uploaderEvents[fileID] = null;
|
||
}
|
||
|
||
if (this.uploaderSockets[fileID]) {
|
||
this.uploaderSockets[fileID].close();
|
||
this.uploaderSockets[fileID] = null;
|
||
}
|
||
}
|
||
|
||
assertHost(method) {
|
||
if (!this.opts.companionUrl) {
|
||
throw new Error(`Expected a \`companionUrl\` option containing a Companion address, or if you are not using Companion, a custom \`${method}\` implementation.`);
|
||
}
|
||
}
|
||
|
||
createMultipartUpload(file) {
|
||
this.assertHost('createMultipartUpload');
|
||
const metadata = {};
|
||
Object.keys(file.meta).forEach(key => {
|
||
if (file.meta[key] != null) {
|
||
metadata[key] = file.meta[key].toString();
|
||
}
|
||
});
|
||
return this.client.post('s3/multipart', {
|
||
filename: file.name,
|
||
type: file.type,
|
||
metadata
|
||
}).then(assertServerError);
|
||
}
|
||
|
||
listParts(file, _ref) {
|
||
let {
|
||
key,
|
||
uploadId
|
||
} = _ref;
|
||
this.assertHost('listParts');
|
||
const filename = encodeURIComponent(key);
|
||
return this.client.get(`s3/multipart/${uploadId}?key=${filename}`).then(assertServerError);
|
||
}
|
||
|
||
prepareUploadParts(file, _ref2) {
|
||
let {
|
||
key,
|
||
uploadId,
|
||
partNumbers
|
||
} = _ref2;
|
||
this.assertHost('prepareUploadParts');
|
||
const filename = encodeURIComponent(key);
|
||
return this.client.get(`s3/multipart/${uploadId}/batch?key=${filename}&partNumbers=${partNumbers.join(',')}`).then(assertServerError);
|
||
}
|
||
|
||
completeMultipartUpload(file, _ref3) {
|
||
let {
|
||
key,
|
||
uploadId,
|
||
parts
|
||
} = _ref3;
|
||
this.assertHost('completeMultipartUpload');
|
||
const filename = encodeURIComponent(key);
|
||
const uploadIdEnc = encodeURIComponent(uploadId);
|
||
return this.client.post(`s3/multipart/${uploadIdEnc}/complete?key=${filename}`, {
|
||
parts
|
||
}).then(assertServerError);
|
||
}
|
||
|
||
abortMultipartUpload(file, _ref4) {
|
||
let {
|
||
key,
|
||
uploadId
|
||
} = _ref4;
|
||
this.assertHost('abortMultipartUpload');
|
||
const filename = encodeURIComponent(key);
|
||
const uploadIdEnc = encodeURIComponent(uploadId);
|
||
return this.client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`).then(assertServerError);
|
||
}
|
||
|
||
uploadFile(file) {
|
||
return new Promise((resolve, reject) => {
|
||
const onStart = data => {
|
||
const cFile = this.uppy.getFile(file.id);
|
||
this.uppy.setFileState(file.id, {
|
||
s3Multipart: { ...cFile.s3Multipart,
|
||
key: data.key,
|
||
uploadId: data.uploadId
|
||
}
|
||
});
|
||
};
|
||
|
||
const onProgress = (bytesUploaded, bytesTotal) => {
|
||
this.uppy.emit('upload-progress', file, {
|
||
uploader: this,
|
||
bytesUploaded,
|
||
bytesTotal
|
||
});
|
||
};
|
||
|
||
const onError = err => {
|
||
this.uppy.log(err);
|
||
this.uppy.emit('upload-error', file, err);
|
||
queuedRequest.done();
|
||
this.resetUploaderReferences(file.id);
|
||
reject(err);
|
||
};
|
||
|
||
const onSuccess = result => {
|
||
const uploadResp = {
|
||
body: { ...result
|
||
},
|
||
uploadURL: result.location
|
||
};
|
||
queuedRequest.done();
|
||
this.resetUploaderReferences(file.id);
|
||
const cFile = this.uppy.getFile(file.id);
|
||
this.uppy.emit('upload-success', cFile || file, uploadResp);
|
||
|
||
if (result.location) {
|
||
this.uppy.log(`Download ${upload.file.name} from ${result.location}`);
|
||
}
|
||
|
||
resolve(upload);
|
||
};
|
||
|
||
const onPartComplete = part => {
|
||
const cFile = this.uppy.getFile(file.id);
|
||
|
||
if (!cFile) {
|
||
return;
|
||
}
|
||
|
||
this.uppy.emit('s3-multipart:part-uploaded', cFile, part);
|
||
};
|
||
|
||
const upload = new MultipartUploader(file.data, {
|
||
// .bind to pass the file object to each handler.
|
||
createMultipartUpload: this.opts.createMultipartUpload.bind(this, file),
|
||
listParts: this.opts.listParts.bind(this, file),
|
||
prepareUploadParts: this.opts.prepareUploadParts.bind(this, file),
|
||
completeMultipartUpload: this.opts.completeMultipartUpload.bind(this, file),
|
||
abortMultipartUpload: this.opts.abortMultipartUpload.bind(this, file),
|
||
getChunkSize: this.opts.getChunkSize ? this.opts.getChunkSize.bind(this) : null,
|
||
onStart,
|
||
onProgress,
|
||
onError,
|
||
onSuccess,
|
||
onPartComplete,
|
||
limit: this.opts.limit || 5,
|
||
retryDelays: this.opts.retryDelays || [],
|
||
...file.s3Multipart
|
||
});
|
||
this.uploaders[file.id] = upload;
|
||
this.uploaderEvents[file.id] = new EventTracker(this.uppy);
|
||
let queuedRequest = this.requests.run(() => {
|
||
if (!file.isPaused) {
|
||
upload.start();
|
||
} // Don't do anything here, the caller will take care of cancelling the upload itself
|
||
// using resetUploaderReferences(). This is because resetUploaderReferences() has to be
|
||
// called when this request is still in the queue, and has not been started yet, too. At
|
||
// that point this cancellation function is not going to be called.
|
||
|
||
|
||
return () => {};
|
||
});
|
||
this.onFileRemove(file.id, removed => {
|
||
queuedRequest.abort();
|
||
this.resetUploaderReferences(file.id, {
|
||
abort: true
|
||
});
|
||
resolve(`upload ${removed.id} was removed`);
|
||
});
|
||
this.onCancelAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
this.resetUploaderReferences(file.id, {
|
||
abort: true
|
||
});
|
||
resolve(`upload ${file.id} was canceled`);
|
||
});
|
||
this.onFilePause(file.id, isPaused => {
|
||
if (isPaused) {
|
||
// Remove this file from the queue so another file can start in its place.
|
||
queuedRequest.abort();
|
||
upload.pause();
|
||
} else {
|
||
// Resuming an upload should be queued, else you could pause and then
|
||
// resume a queued upload to make it skip the queue.
|
||
queuedRequest.abort();
|
||
queuedRequest = this.requests.run(() => {
|
||
upload.start();
|
||
return () => {};
|
||
});
|
||
}
|
||
});
|
||
this.onPauseAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
upload.pause();
|
||
});
|
||
this.onResumeAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
|
||
if (file.error) {
|
||
upload.abort();
|
||
}
|
||
|
||
queuedRequest = this.requests.run(() => {
|
||
upload.start();
|
||
return () => {};
|
||
});
|
||
}); // Don't double-emit upload-started for Golden Retriever-restored files that were already started
|
||
|
||
if (!file.progress.uploadStarted || !file.isRestored) {
|
||
this.uppy.emit('upload-started', file);
|
||
}
|
||
});
|
||
}
|
||
|
||
uploadRemote(file) {
|
||
this.resetUploaderReferences(file.id); // Don't double-emit upload-started for Golden Retriever-restored files that were already started
|
||
|
||
if (!file.progress.uploadStarted || !file.isRestored) {
|
||
this.uppy.emit('upload-started', file);
|
||
}
|
||
|
||
if (file.serverToken) {
|
||
return this.connectToServerSocket(file);
|
||
}
|
||
|
||
return new Promise((resolve, reject) => {
|
||
const Client = file.remote.providerOptions.provider ? Provider : RequestClient;
|
||
const client = new Client(this.uppy, file.remote.providerOptions);
|
||
client.post(file.remote.url, { ...file.remote.body,
|
||
protocol: 's3-multipart',
|
||
size: file.data.size,
|
||
metadata: file.meta
|
||
}).then(res => {
|
||
this.uppy.setFileState(file.id, {
|
||
serverToken: res.token
|
||
});
|
||
file = this.uppy.getFile(file.id);
|
||
return file;
|
||
}).then(file => {
|
||
return this.connectToServerSocket(file);
|
||
}).then(() => {
|
||
resolve();
|
||
}).catch(err => {
|
||
this.uppy.emit('upload-error', file, err);
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
connectToServerSocket(file) {
|
||
return new Promise((resolve, reject) => {
|
||
const token = file.serverToken;
|
||
const host = getSocketHost(file.remote.companionUrl);
|
||
const socket = new Socket({
|
||
target: `${host}/api/${token}`,
|
||
autoOpen: false
|
||
});
|
||
this.uploaderSockets[file.id] = socket;
|
||
this.uploaderEvents[file.id] = new EventTracker(this.uppy);
|
||
this.onFileRemove(file.id, () => {
|
||
queuedRequest.abort();
|
||
socket.send('cancel', {});
|
||
this.resetUploaderReferences(file.id, {
|
||
abort: true
|
||
});
|
||
resolve(`upload ${file.id} was removed`);
|
||
});
|
||
this.onFilePause(file.id, isPaused => {
|
||
if (isPaused) {
|
||
// Remove this file from the queue so another file can start in its place.
|
||
queuedRequest.abort();
|
||
socket.send('pause', {});
|
||
} else {
|
||
// Resuming an upload should be queued, else you could pause and then
|
||
// resume a queued upload to make it skip the queue.
|
||
queuedRequest.abort();
|
||
queuedRequest = this.requests.run(() => {
|
||
socket.send('resume', {});
|
||
return () => {};
|
||
});
|
||
}
|
||
});
|
||
this.onPauseAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
socket.send('pause', {});
|
||
});
|
||
this.onCancelAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
socket.send('cancel', {});
|
||
this.resetUploaderReferences(file.id);
|
||
resolve(`upload ${file.id} was canceled`);
|
||
});
|
||
this.onResumeAll(file.id, () => {
|
||
queuedRequest.abort();
|
||
|
||
if (file.error) {
|
||
socket.send('pause', {});
|
||
}
|
||
|
||
queuedRequest = this.requests.run(() => {
|
||
socket.send('resume', {});
|
||
});
|
||
});
|
||
this.onRetry(file.id, () => {
|
||
// Only do the retry if the upload is actually in progress;
|
||
// else we could try to send these messages when the upload is still queued.
|
||
// We may need a better check for this since the socket may also be closed
|
||
// for other reasons, like network failures.
|
||
if (socket.isOpen) {
|
||
socket.send('pause', {});
|
||
socket.send('resume', {});
|
||
}
|
||
});
|
||
this.onRetryAll(file.id, () => {
|
||
if (socket.isOpen) {
|
||
socket.send('pause', {});
|
||
socket.send('resume', {});
|
||
}
|
||
});
|
||
socket.on('progress', progressData => emitSocketProgress(this, progressData, file));
|
||
socket.on('error', errData => {
|
||
this.uppy.emit('upload-error', file, new Error(errData.error));
|
||
this.resetUploaderReferences(file.id);
|
||
queuedRequest.done();
|
||
reject(new Error(errData.error));
|
||
});
|
||
socket.on('success', data => {
|
||
const uploadResp = {
|
||
uploadURL: data.url
|
||
};
|
||
this.uppy.emit('upload-success', file, uploadResp);
|
||
this.resetUploaderReferences(file.id);
|
||
queuedRequest.done();
|
||
resolve();
|
||
});
|
||
let queuedRequest = this.requests.run(() => {
|
||
socket.open();
|
||
|
||
if (file.isPaused) {
|
||
socket.send('pause', {});
|
||
}
|
||
|
||
return () => {};
|
||
});
|
||
});
|
||
}
|
||
|
||
upload(fileIDs) {
|
||
if (fileIDs.length === 0) return Promise.resolve();
|
||
const promises = fileIDs.map(id => {
|
||
const file = this.uppy.getFile(id);
|
||
|
||
if (file.isRemote) {
|
||
return this.uploadRemote(file);
|
||
}
|
||
|
||
return this.uploadFile(file);
|
||
});
|
||
return Promise.all(promises);
|
||
}
|
||
|
||
onFileRemove(fileID, cb) {
|
||
this.uploaderEvents[fileID].on('file-removed', file => {
|
||
if (fileID === file.id) cb(file.id);
|
||
});
|
||
}
|
||
|
||
onFilePause(fileID, cb) {
|
||
this.uploaderEvents[fileID].on('upload-pause', (targetFileID, isPaused) => {
|
||
if (fileID === targetFileID) {
|
||
// const isPaused = this.uppy.pauseResume(fileID)
|
||
cb(isPaused);
|
||
}
|
||
});
|
||
}
|
||
|
||
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();
|
||
});
|
||
}
|
||
|
||
onPauseAll(fileID, cb) {
|
||
this.uploaderEvents[fileID].on('pause-all', () => {
|
||
if (!this.uppy.getFile(fileID)) return;
|
||
cb();
|
||
});
|
||
}
|
||
|
||
onCancelAll(fileID, cb) {
|
||
this.uploaderEvents[fileID].on('cancel-all', () => {
|
||
if (!this.uppy.getFile(fileID)) return;
|
||
cb();
|
||
});
|
||
}
|
||
|
||
onResumeAll(fileID, cb) {
|
||
this.uploaderEvents[fileID].on('resume-all', () => {
|
||
if (!this.uppy.getFile(fileID)) return;
|
||
cb();
|
||
});
|
||
}
|
||
|
||
install() {
|
||
const {
|
||
capabilities
|
||
} = this.uppy.getState();
|
||
this.uppy.setState({
|
||
capabilities: { ...capabilities,
|
||
resumableUploads: true
|
||
}
|
||
});
|
||
this.uppy.addUploader(this.upload);
|
||
}
|
||
|
||
uninstall() {
|
||
const {
|
||
capabilities
|
||
} = this.uppy.getState();
|
||
this.uppy.setState({
|
||
capabilities: { ...capabilities,
|
||
resumableUploads: false
|
||
}
|
||
});
|
||
this.uppy.removeUploader(this.upload);
|
||
}
|
||
|
||
}, _class.VERSION = "2.2.1", _temp);
|
||
},{"./MultipartUploader":2,"@uppy/companion-client":13,"@uppy/core/lib/BasePlugin":15,"@uppy/utils/lib/EventTracker":27,"@uppy/utils/lib/RateLimitedQueue":30,"@uppy/utils/lib/emitSocketProgress":33,"@uppy/utils/lib/getSocketHost":44}],4:[function(require,module,exports){
|
||
var _getOptions, _addEventHandlerForFile, _addEventHandlerIfFileStillExists, _uploadLocalFile, _uploadRemoteFile;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const {
|
||
nanoid
|
||
} = require('nanoid/non-secure');
|
||
|
||
const {
|
||
Provider,
|
||
RequestClient,
|
||
Socket
|
||
} = require('@uppy/companion-client');
|
||
|
||
const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress');
|
||
|
||
const getSocketHost = require('@uppy/utils/lib/getSocketHost');
|
||
|
||
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 {
|
||
internalRateLimitedQueue
|
||
} = require('@uppy/utils/lib/RateLimitedQueue'); // See XHRUpload
|
||
|
||
|
||
function buildResponseError(xhr, error) {
|
||
if (isNetworkError(xhr)) return new NetworkError(error, xhr); // TODO: when we drop support for browsers that do not support this syntax, use:
|
||
// return new Error('Upload error', { cause: error, request: xhr })
|
||
|
||
const err = new Error('Upload error');
|
||
err.cause = error;
|
||
err.request = xhr;
|
||
return err;
|
||
} // See XHRUpload
|
||
|
||
|
||
function setTypeInBlob(file) {
|
||
const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type);
|
||
return dataWithUpdatedType;
|
||
}
|
||
|
||
function addMetadata(formData, meta, opts) {
|
||
const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default.
|
||
: Object.keys(meta);
|
||
metaFields.forEach(item => {
|
||
formData.append(item, meta[item]);
|
||
});
|
||
}
|
||
|
||
function createFormDataUpload(file, opts) {
|
||
const formPost = new FormData();
|
||
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;
|
||
}
|
||
|
||
const createBareUpload = file => file.data;
|
||
|
||
module.exports = (_getOptions = /*#__PURE__*/_classPrivateFieldLooseKey("getOptions"), _addEventHandlerForFile = /*#__PURE__*/_classPrivateFieldLooseKey("addEventHandlerForFile"), _addEventHandlerIfFileStillExists = /*#__PURE__*/_classPrivateFieldLooseKey("addEventHandlerIfFileStillExists"), _uploadLocalFile = /*#__PURE__*/_classPrivateFieldLooseKey("uploadLocalFile"), _uploadRemoteFile = /*#__PURE__*/_classPrivateFieldLooseKey("uploadRemoteFile"), class MiniXHRUpload {
|
||
constructor(_uppy, _opts) {
|
||
Object.defineProperty(this, _uploadRemoteFile, {
|
||
value: _uploadRemoteFile2
|
||
});
|
||
Object.defineProperty(this, _uploadLocalFile, {
|
||
value: _uploadLocalFile2
|
||
});
|
||
Object.defineProperty(this, _addEventHandlerIfFileStillExists, {
|
||
value: _addEventHandlerIfFileStillExists2
|
||
});
|
||
Object.defineProperty(this, _addEventHandlerForFile, {
|
||
value: _addEventHandlerForFile2
|
||
});
|
||
Object.defineProperty(this, _getOptions, {
|
||
value: _getOptions2
|
||
});
|
||
this.uppy = _uppy;
|
||
this.opts = {
|
||
validateStatus(status) {
|
||
return status >= 200 && status < 300;
|
||
},
|
||
|
||
..._opts
|
||
};
|
||
this.requests = _opts[internalRateLimitedQueue];
|
||
this.uploaderEvents = Object.create(null);
|
||
this.i18n = _opts.i18n;
|
||
}
|
||
|
||
uploadFile(id, current, total) {
|
||
const file = this.uppy.getFile(id);
|
||
|
||
if (file.error) {
|
||
throw new Error(file.error);
|
||
} else if (file.isRemote) {
|
||
return _classPrivateFieldLooseBase(this, _uploadRemoteFile)[_uploadRemoteFile](file, current, total);
|
||
}
|
||
|
||
return _classPrivateFieldLooseBase(this, _uploadLocalFile)[_uploadLocalFile](file, current, total);
|
||
}
|
||
|
||
});
|
||
|
||
function _getOptions2(file) {
|
||
var _file$xhrUpload;
|
||
|
||
const {
|
||
uppy
|
||
} = this;
|
||
const overrides = uppy.getState().xhrUpload;
|
||
const opts = { ...this.opts,
|
||
...(overrides || {}),
|
||
...(file.xhrUpload || {}),
|
||
headers: { ...this.opts.headers,
|
||
...(overrides == null ? void 0 : overrides.headers),
|
||
...((_file$xhrUpload = file.xhrUpload) == null ? void 0 : _file$xhrUpload.headers)
|
||
}
|
||
};
|
||
return opts;
|
||
}
|
||
|
||
function _addEventHandlerForFile2(eventName, fileID, eventHandler) {
|
||
this.uploaderEvents[fileID].on(eventName, targetFileID => {
|
||
if (fileID === targetFileID) eventHandler();
|
||
});
|
||
}
|
||
|
||
function _addEventHandlerIfFileStillExists2(eventName, fileID, eventHandler) {
|
||
this.uploaderEvents[fileID].on(eventName, () => {
|
||
if (this.uppy.getFile(fileID)) eventHandler();
|
||
});
|
||
}
|
||
|
||
function _uploadLocalFile2(file, current, total) {
|
||
const opts = _classPrivateFieldLooseBase(this, _getOptions)[_getOptions](file);
|
||
|
||
this.uppy.log(`uploading ${current} of ${total}`);
|
||
return new Promise((resolve, reject) => {
|
||
// This is done in index.js in the S3 plugin.
|
||
// this.uppy.emit('upload-started', file)
|
||
const data = opts.formData ? createFormDataUpload(file, opts) : createBareUpload(file, opts);
|
||
const xhr = new XMLHttpRequest();
|
||
this.uploaderEvents[file.id] = new EventTracker(this.uppy);
|
||
const timer = new ProgressTimeout(opts.timeout, () => {
|
||
xhr.abort(); // eslint-disable-next-line no-use-before-define
|
||
|
||
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 = nanoid();
|
||
xhr.upload.addEventListener('loadstart', () => {
|
||
this.uppy.log(`[AwsS3/XHRUpload] ${id} started`);
|
||
});
|
||
xhr.upload.addEventListener('progress', ev => {
|
||
this.uppy.log(`[AwsS3/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', ev => {
|
||
this.uppy.log(`[AwsS3/XHRUpload] ${id} finished`);
|
||
timer.done(); // eslint-disable-next-line no-use-before-define
|
||
|
||
queuedRequest.done();
|
||
|
||
if (this.uploaderEvents[file.id]) {
|
||
this.uploaderEvents[file.id].remove();
|
||
this.uploaderEvents[file.id] = null;
|
||
}
|
||
|
||
if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) {
|
||
const body = opts.getResponseData(xhr.responseText, xhr);
|
||
const uploadURL = body[opts.responseUrlFieldName];
|
||
const uploadResp = {
|
||
status: ev.target.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: ev.target.status,
|
||
body
|
||
};
|
||
this.uppy.emit('upload-error', file, error, response);
|
||
return reject(error);
|
||
});
|
||
xhr.addEventListener('error', () => {
|
||
this.uppy.log(`[AwsS3/XHRUpload] ${id} errored`);
|
||
timer.done(); // eslint-disable-next-line no-use-before-define
|
||
|
||
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. It’s important to set withCredentials
|
||
// to a boolean, otherwise React Native crashes
|
||
|
||
xhr.withCredentials = Boolean(opts.withCredentials);
|
||
|
||
if (opts.responseType !== '') {
|
||
xhr.responseType = opts.responseType;
|
||
}
|
||
|
||
Object.keys(opts.headers).forEach(header => {
|
||
xhr.setRequestHeader(header, opts.headers[header]);
|
||
});
|
||
const queuedRequest = this.requests.run(() => {
|
||
xhr.send(data);
|
||
return () => {
|
||
// eslint-disable-next-line no-use-before-define
|
||
timer.done();
|
||
xhr.abort();
|
||
};
|
||
}, {
|
||
priority: 1
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('file-removed', file.id, () => {
|
||
queuedRequest.abort();
|
||
reject(new Error('File removed'));
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('cancel-all', file.id, () => {
|
||
queuedRequest.abort();
|
||
reject(new Error('Upload cancelled'));
|
||
});
|
||
});
|
||
}
|
||
|
||
function _uploadRemoteFile2(file) {
|
||
const opts = _classPrivateFieldLooseBase(this, _getOptions)[_getOptions](file); // This is done in index.js in the S3 plugin.
|
||
// this.uppy.emit('upload-started', file)
|
||
|
||
|
||
const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default.
|
||
: Object.keys(file.meta);
|
||
const Client = file.remote.providerOptions.provider ? Provider : RequestClient;
|
||
const client = new Client(this.uppy, file.remote.providerOptions);
|
||
return client.post(file.remote.url, { ...file.remote.body,
|
||
endpoint: opts.endpoint,
|
||
size: file.data.size,
|
||
fieldname: opts.fieldName,
|
||
metadata: Object.fromEntries(metaFields.map(name => [name, file.meta[name]])),
|
||
httpMethod: opts.method,
|
||
useFormData: opts.formData,
|
||
headers: opts.headers
|
||
}).then(res => new Promise((resolve, reject) => {
|
||
const {
|
||
token
|
||
} = res;
|
||
const host = getSocketHost(file.remote.companionUrl);
|
||
const socket = new Socket({
|
||
target: `${host}/api/${token}`,
|
||
autoOpen: false
|
||
});
|
||
this.uploaderEvents[file.id] = new EventTracker(this.uppy);
|
||
const queuedRequest = this.requests.run(() => {
|
||
socket.open();
|
||
|
||
if (file.isPaused) {
|
||
socket.send('pause', {});
|
||
}
|
||
|
||
return () => socket.close();
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('file-removed', file.id, () => {
|
||
socket.send('cancel', {});
|
||
queuedRequest.abort();
|
||
resolve(`upload ${file.id} was removed`);
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('cancel-all', file.id, () => {
|
||
socket.send('cancel', {});
|
||
queuedRequest.abort();
|
||
resolve(`upload ${file.id} was canceled`);
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('upload-retry', file.id, () => {
|
||
socket.send('pause', {});
|
||
socket.send('resume', {});
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('retry-all', 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,
|
||
bytesUploaded: data.bytesUploaded
|
||
};
|
||
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);
|
||
});
|
||
}).catch(err => {
|
||
this.uppy.emit('upload-error', file, err);
|
||
return Promise.reject(err);
|
||
}));
|
||
}
|
||
},{"@uppy/companion-client":13,"@uppy/utils/lib/EventTracker":27,"@uppy/utils/lib/NetworkError":28,"@uppy/utils/lib/ProgressTimeout":29,"@uppy/utils/lib/RateLimitedQueue":30,"@uppy/utils/lib/emitSocketProgress":33,"@uppy/utils/lib/getSocketHost":44,"@uppy/utils/lib/isNetworkError":48,"nanoid/non-secure":57}],5:[function(require,module,exports){
|
||
var _class, _client, _requests, _uploader, _handleUpload, _temp;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
/**
|
||
* This plugin is currently a A Big Hack™! The core reason for that is how this plugin
|
||
* interacts with Uppy's current pipeline design. The pipeline can handle files in steps,
|
||
* including preprocessing, uploading, and postprocessing steps. This plugin initially
|
||
* was designed to do its work in a preprocessing step, and let XHRUpload deal with the
|
||
* actual file upload as an uploading step. However, Uppy runs steps on all files at once,
|
||
* sequentially: first, all files go through a preprocessing step, then, once they are all
|
||
* done, they go through the uploading step.
|
||
*
|
||
* For S3, this causes severely broken behaviour when users upload many files. The
|
||
* preprocessing step will request S3 upload URLs that are valid for a short time only,
|
||
* but it has to do this for _all_ files, which can take a long time if there are hundreds
|
||
* or even thousands of files. By the time the uploader step starts, the first URLs may
|
||
* already have expired. If not, the uploading might take such a long time that later URLs
|
||
* will expire before some files can be uploaded.
|
||
*
|
||
* The long-term solution to this problem is to change the upload pipeline so that files
|
||
* can be sent to the next step individually. That requires a breaking change, so it is
|
||
* planned for some future Uppy version.
|
||
*
|
||
* In the mean time, this plugin is stuck with a hackier approach: the necessary parts
|
||
* of the XHRUpload implementation were copied into this plugin, as the MiniXHRUpload
|
||
* class, and this plugin calls into it immediately once it receives an upload URL.
|
||
* This isn't as nicely modular as we'd like and requires us to maintain two copies of
|
||
* the XHRUpload code, but at least it's not horrifically broken :)
|
||
*/
|
||
const BasePlugin = require('@uppy/core/lib/BasePlugin');
|
||
|
||
const {
|
||
RateLimitedQueue,
|
||
internalRateLimitedQueue
|
||
} = require('@uppy/utils/lib/RateLimitedQueue');
|
||
|
||
const {
|
||
RequestClient
|
||
} = require('@uppy/companion-client');
|
||
|
||
const MiniXHRUpload = require('./MiniXHRUpload');
|
||
|
||
const isXml = require('./isXml');
|
||
|
||
const locale = require('./locale');
|
||
|
||
function resolveUrl(origin, link) {
|
||
return new URL(link, origin || undefined).toString();
|
||
}
|
||
/**
|
||
* Get the contents of a named tag in an XML source string.
|
||
*
|
||
* @param {string} source - The XML source string.
|
||
* @param {string} tagName - The name of the tag.
|
||
* @returns {string} The contents of the tag, or the empty string if the tag does not exist.
|
||
*/
|
||
|
||
|
||
function getXmlValue(source, tagName) {
|
||
const start = source.indexOf(`<${tagName}>`);
|
||
const end = source.indexOf(`</${tagName}>`, start);
|
||
return start !== -1 && end !== -1 ? source.slice(start + tagName.length + 2, end) : '';
|
||
}
|
||
|
||
function assertServerError(res) {
|
||
if (res && res.error) {
|
||
const error = new Error(res.message);
|
||
Object.assign(error, res.error);
|
||
throw error;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
function validateParameters(file, params) {
|
||
const valid = params != null && typeof params.url === 'string' && (typeof params.fields === 'object' || params.fields == null);
|
||
|
||
if (!valid) {
|
||
const err = new TypeError(`AwsS3: got incorrect result from 'getUploadParameters()' for file '${file.name}', expected an object '{ url, method, fields, headers }' but got '${JSON.stringify(params)}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`);
|
||
throw err;
|
||
}
|
||
|
||
const methodIsValid = params.method == null || /^p(u|os)t$/i.test(params.method);
|
||
|
||
if (!methodIsValid) {
|
||
const err = new TypeError(`AwsS3: got incorrect method from 'getUploadParameters()' for file '${file.name}', expected 'put' or 'post' but got '${params.method}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`);
|
||
throw err;
|
||
}
|
||
} // Get the error data from a failed XMLHttpRequest instance.
|
||
// `content` is the S3 response as a string.
|
||
// `xhr` is the XMLHttpRequest instance.
|
||
|
||
|
||
function defaultGetResponseError(content, xhr) {
|
||
// If no response, we don't have a specific error message, use the default.
|
||
if (!isXml(content, xhr)) {
|
||
return undefined;
|
||
}
|
||
|
||
const error = getXmlValue(content, 'Message');
|
||
return new Error(error);
|
||
} // warning deduplication flag: see `getResponseData()` XHRUpload option definition
|
||
|
||
|
||
let warnedSuccessActionStatus = false;
|
||
module.exports = (_temp = (_client = /*#__PURE__*/_classPrivateFieldLooseKey("client"), _requests = /*#__PURE__*/_classPrivateFieldLooseKey("requests"), _uploader = /*#__PURE__*/_classPrivateFieldLooseKey("uploader"), _handleUpload = /*#__PURE__*/_classPrivateFieldLooseKey("handleUpload"), _class = class AwsS3 extends BasePlugin {
|
||
// eslint-disable-next-line global-require
|
||
constructor(uppy, opts) {
|
||
super(uppy, opts);
|
||
Object.defineProperty(this, _client, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _requests, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _uploader, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _handleUpload, {
|
||
writable: true,
|
||
value: fileIDs => {
|
||
/**
|
||
* keep track of `getUploadParameters()` responses
|
||
* so we can cancel the calls individually using just a file ID
|
||
*
|
||
* @type {object.<string, Promise>}
|
||
*/
|
||
const paramsPromises = Object.create(null);
|
||
|
||
function onremove(file) {
|
||
var _paramsPromises$id;
|
||
|
||
const {
|
||
id
|
||
} = file;
|
||
(_paramsPromises$id = paramsPromises[id]) == null ? void 0 : _paramsPromises$id.abort();
|
||
}
|
||
|
||
this.uppy.on('file-removed', onremove);
|
||
fileIDs.forEach(id => {
|
||
const file = this.uppy.getFile(id);
|
||
this.uppy.emit('upload-started', file);
|
||
});
|
||
|
||
const getUploadParameters = _classPrivateFieldLooseBase(this, _requests)[_requests].wrapPromiseFunction(file => {
|
||
return this.opts.getUploadParameters(file);
|
||
});
|
||
|
||
const numberOfFiles = fileIDs.length;
|
||
return Promise.allSettled(fileIDs.map((id, index) => {
|
||
paramsPromises[id] = getUploadParameters(this.uppy.getFile(id));
|
||
return paramsPromises[id].then(params => {
|
||
delete paramsPromises[id];
|
||
const file = this.uppy.getFile(id);
|
||
validateParameters(file, params);
|
||
const {
|
||
method = 'post',
|
||
url,
|
||
fields,
|
||
headers
|
||
} = params;
|
||
const xhrOpts = {
|
||
method,
|
||
formData: method.toLowerCase() === 'post',
|
||
endpoint: url,
|
||
metaFields: fields ? Object.keys(fields) : []
|
||
};
|
||
|
||
if (headers) {
|
||
xhrOpts.headers = headers;
|
||
}
|
||
|
||
this.uppy.setFileState(file.id, {
|
||
meta: { ...file.meta,
|
||
...fields
|
||
},
|
||
xhrUpload: xhrOpts
|
||
});
|
||
return _classPrivateFieldLooseBase(this, _uploader)[_uploader].uploadFile(file.id, index, numberOfFiles);
|
||
}).catch(error => {
|
||
delete paramsPromises[id];
|
||
const file = this.uppy.getFile(id);
|
||
this.uppy.emit('upload-error', file, error);
|
||
return Promise.reject(error);
|
||
});
|
||
})).finally(() => {
|
||
// cleanup.
|
||
this.uppy.off('file-removed', onremove);
|
||
});
|
||
}
|
||
});
|
||
this.type = 'uploader';
|
||
this.id = this.opts.id || 'AwsS3';
|
||
this.title = 'AWS S3';
|
||
this.defaultLocale = locale;
|
||
const defaultOptions = {
|
||
timeout: 30 * 1000,
|
||
limit: 0,
|
||
metaFields: [],
|
||
// have to opt in
|
||
getUploadParameters: this.getUploadParameters.bind(this)
|
||
};
|
||
this.opts = { ...defaultOptions,
|
||
...opts
|
||
}; // TODO: remove i18n once we can depend on XHRUpload instead of MiniXHRUpload
|
||
|
||
this.i18nInit();
|
||
_classPrivateFieldLooseBase(this, _client)[_client] = new RequestClient(uppy, opts);
|
||
_classPrivateFieldLooseBase(this, _requests)[_requests] = new RateLimitedQueue(this.opts.limit);
|
||
}
|
||
|
||
getUploadParameters(file) {
|
||
if (!this.opts.companionUrl) {
|
||
throw new Error('Expected a `companionUrl` option containing a Companion address.');
|
||
}
|
||
|
||
const filename = file.meta.name;
|
||
const {
|
||
type
|
||
} = file.meta;
|
||
const metadata = Object.fromEntries(this.opts.metaFields.filter(key => file.meta[key] != null).map(key => [`metadata[${key}]`, file.meta[key].toString()]));
|
||
const query = new URLSearchParams({
|
||
filename,
|
||
type,
|
||
...metadata
|
||
});
|
||
return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/params?${query}`).then(assertServerError);
|
||
}
|
||
|
||
install() {
|
||
const {
|
||
uppy
|
||
} = this;
|
||
uppy.addUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]); // Get the response data from a successful XMLHttpRequest instance.
|
||
// `content` is the S3 response as a string.
|
||
// `xhr` is the XMLHttpRequest instance.
|
||
|
||
function defaultGetResponseData(content, xhr) {
|
||
const opts = this; // If no response, we've hopefully done a PUT request to the file
|
||
// in the bucket on its full URL.
|
||
|
||
if (!isXml(content, xhr)) {
|
||
if (opts.method.toUpperCase() === 'POST') {
|
||
if (!warnedSuccessActionStatus) {
|
||
uppy.log('[AwsS3] No response data found, make sure to set the success_action_status AWS SDK option to 201. See https://uppy.io/docs/aws-s3/#POST-Uploads', 'warning');
|
||
warnedSuccessActionStatus = true;
|
||
} // The responseURL won't contain the object key. Give up.
|
||
|
||
|
||
return {
|
||
location: null
|
||
};
|
||
} // responseURL is not available in older browsers.
|
||
|
||
|
||
if (!xhr.responseURL) {
|
||
return {
|
||
location: null
|
||
};
|
||
} // Trim the query string because it's going to be a bunch of presign
|
||
// parameters for a PUT request—doing a GET request with those will
|
||
// always result in an error
|
||
|
||
|
||
return {
|
||
location: xhr.responseURL.replace(/\?.*$/, '')
|
||
};
|
||
}
|
||
|
||
return {
|
||
// Some S3 alternatives do not reply with an absolute URL.
|
||
// Eg DigitalOcean Spaces uses /$bucketName/xyz
|
||
location: resolveUrl(xhr.responseURL, getXmlValue(content, 'Location')),
|
||
bucket: getXmlValue(content, 'Bucket'),
|
||
key: getXmlValue(content, 'Key'),
|
||
etag: getXmlValue(content, 'ETag')
|
||
};
|
||
}
|
||
|
||
const xhrOptions = {
|
||
fieldName: 'file',
|
||
responseUrlFieldName: 'location',
|
||
timeout: this.opts.timeout,
|
||
// Share the rate limiting queue with XHRUpload.
|
||
[internalRateLimitedQueue]: _classPrivateFieldLooseBase(this, _requests)[_requests],
|
||
responseType: 'text',
|
||
getResponseData: this.opts.getResponseData || defaultGetResponseData,
|
||
getResponseError: defaultGetResponseError
|
||
}; // TODO: remove i18n once we can depend on XHRUpload instead of MiniXHRUpload
|
||
|
||
xhrOptions.i18n = this.i18n; // Revert to `uppy.use(XHRUpload)` once the big comment block at the top of
|
||
// this file is solved
|
||
|
||
_classPrivateFieldLooseBase(this, _uploader)[_uploader] = new MiniXHRUpload(uppy, xhrOptions);
|
||
}
|
||
|
||
uninstall() {
|
||
this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]);
|
||
}
|
||
|
||
}), _class.VERSION = "2.0.8", _temp);
|
||
},{"./MiniXHRUpload":4,"./isXml":6,"./locale":7,"@uppy/companion-client":13,"@uppy/core/lib/BasePlugin":15,"@uppy/utils/lib/RateLimitedQueue":30}],6:[function(require,module,exports){
|
||
/**
|
||
* Remove parameters like `charset=utf-8` from the end of a mime type string.
|
||
*
|
||
* @param {string} mimeType - The mime type string that may have optional parameters.
|
||
* @returns {string} The "base" mime type, i.e. only 'category/type'.
|
||
*/
|
||
function removeMimeParameters(mimeType) {
|
||
return mimeType.replace(/;.*$/, '');
|
||
}
|
||
/**
|
||
* Check if a response contains XML based on the response object and its text content.
|
||
*
|
||
* @param {string} content - The text body of the response.
|
||
* @param {object|XMLHttpRequest} xhr - The XHR object or response object from Companion.
|
||
* @returns {bool} Whether the content is (probably) XML.
|
||
*/
|
||
|
||
|
||
function isXml(content, xhr) {
|
||
const rawContentType = xhr.headers ? xhr.headers['content-type'] : xhr.getResponseHeader('Content-Type');
|
||
|
||
if (typeof rawContentType === 'string') {
|
||
const contentType = removeMimeParameters(rawContentType).toLowerCase();
|
||
|
||
if (contentType === 'application/xml' || contentType === 'text/xml') {
|
||
return true;
|
||
} // GCS uses text/html for some reason
|
||
// https://github.com/transloadit/uppy/issues/896
|
||
|
||
|
||
if (contentType === 'text/html' && /^<\?xml /.test(content)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
module.exports = isXml;
|
||
},{}],7:[function(require,module,exports){
|
||
module.exports = {
|
||
strings: {
|
||
timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
|
||
}
|
||
};
|
||
},{}],8:[function(require,module,exports){
|
||
'use strict';
|
||
|
||
class AuthError extends Error {
|
||
constructor() {
|
||
super('Authorization required');
|
||
this.name = 'AuthError';
|
||
this.isAuthError = true;
|
||
}
|
||
|
||
}
|
||
|
||
module.exports = AuthError;
|
||
},{}],9:[function(require,module,exports){
|
||
'use strict';
|
||
|
||
const RequestClient = require('./RequestClient');
|
||
|
||
const tokenStorage = require('./tokenStorage');
|
||
|
||
const getName = id => {
|
||
return id.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
|
||
};
|
||
|
||
module.exports = class Provider extends RequestClient {
|
||
constructor(uppy, opts) {
|
||
super(uppy, opts);
|
||
this.provider = opts.provider;
|
||
this.id = this.provider;
|
||
this.name = this.opts.name || getName(this.id);
|
||
this.pluginId = this.opts.pluginId;
|
||
this.tokenKey = `companion-${this.pluginId}-auth-token`;
|
||
this.companionKeysParams = this.opts.companionKeysParams;
|
||
this.preAuthToken = null;
|
||
}
|
||
|
||
headers() {
|
||
return Promise.all([super.headers(), this.getAuthToken()]).then(_ref => {
|
||
let [headers, token] = _ref;
|
||
const authHeaders = {};
|
||
|
||
if (token) {
|
||
authHeaders['uppy-auth-token'] = token;
|
||
}
|
||
|
||
if (this.companionKeysParams) {
|
||
authHeaders['uppy-credentials-params'] = btoa(JSON.stringify({
|
||
params: this.companionKeysParams
|
||
}));
|
||
}
|
||
|
||
return { ...headers,
|
||
...authHeaders
|
||
};
|
||
});
|
||
}
|
||
|
||
onReceiveResponse(response) {
|
||
response = super.onReceiveResponse(response);
|
||
const plugin = this.uppy.getPlugin(this.pluginId);
|
||
const oldAuthenticated = plugin.getPluginState().authenticated;
|
||
const authenticated = oldAuthenticated ? response.status !== 401 : response.status < 400;
|
||
plugin.setPluginState({
|
||
authenticated
|
||
});
|
||
return response;
|
||
}
|
||
|
||
setAuthToken(token) {
|
||
return this.uppy.getPlugin(this.pluginId).storage.setItem(this.tokenKey, token);
|
||
}
|
||
|
||
getAuthToken() {
|
||
return this.uppy.getPlugin(this.pluginId).storage.getItem(this.tokenKey);
|
||
}
|
||
/**
|
||
* Ensure we have a preauth token if necessary. Attempts to fetch one if we don't,
|
||
* or rejects if loading one fails.
|
||
*/
|
||
|
||
|
||
async ensurePreAuth() {
|
||
if (this.companionKeysParams && !this.preAuthToken) {
|
||
await this.fetchPreAuthToken();
|
||
|
||
if (!this.preAuthToken) {
|
||
throw new Error('Could not load authentication data required for third-party login. Please try again later.');
|
||
}
|
||
}
|
||
}
|
||
|
||
authUrl(queries) {
|
||
if (queries === void 0) {
|
||
queries = {};
|
||
}
|
||
|
||
const params = new URLSearchParams(queries);
|
||
|
||
if (this.preAuthToken) {
|
||
params.set('uppyPreAuthToken', this.preAuthToken);
|
||
}
|
||
|
||
return `${this.hostname}/${this.id}/connect?${params}`;
|
||
}
|
||
|
||
fileUrl(id) {
|
||
return `${this.hostname}/${this.id}/get/${id}`;
|
||
}
|
||
|
||
async fetchPreAuthToken() {
|
||
if (!this.companionKeysParams) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const res = await this.post(`${this.id}/preauth/`, {
|
||
params: this.companionKeysParams
|
||
});
|
||
this.preAuthToken = res.token;
|
||
} catch (err) {
|
||
this.uppy.log(`[CompanionClient] unable to fetch preAuthToken ${err}`, 'warning');
|
||
}
|
||
}
|
||
|
||
list(directory) {
|
||
return this.get(`${this.id}/list/${directory || ''}`);
|
||
}
|
||
|
||
logout() {
|
||
return this.get(`${this.id}/logout`).then(response => Promise.all([response, this.uppy.getPlugin(this.pluginId).storage.removeItem(this.tokenKey)])).then(_ref2 => {
|
||
let [response] = _ref2;
|
||
return response;
|
||
});
|
||
}
|
||
|
||
static initPlugin(plugin, opts, defaultOpts) {
|
||
plugin.type = 'acquirer';
|
||
plugin.files = [];
|
||
|
||
if (defaultOpts) {
|
||
plugin.opts = { ...defaultOpts,
|
||
...opts
|
||
};
|
||
}
|
||
|
||
if (opts.serverUrl || opts.serverPattern) {
|
||
throw new Error('`serverUrl` and `serverPattern` have been renamed to `companionUrl` and `companionAllowedHosts` respectively in the 0.30.5 release. Please consult the docs (for example, https://uppy.io/docs/instagram/ for the Instagram plugin) and use the updated options.`');
|
||
}
|
||
|
||
if (opts.companionAllowedHosts) {
|
||
const pattern = opts.companionAllowedHosts; // validate companionAllowedHosts param
|
||
|
||
if (typeof pattern !== 'string' && !Array.isArray(pattern) && !(pattern instanceof RegExp)) {
|
||
throw new TypeError(`${plugin.id}: the option "companionAllowedHosts" must be one of string, Array, RegExp`);
|
||
}
|
||
|
||
plugin.opts.companionAllowedHosts = pattern;
|
||
} else if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) {
|
||
// does not start with https://
|
||
plugin.opts.companionAllowedHosts = `https://${opts.companionUrl.replace(/^\/\//, '')}`;
|
||
} else {
|
||
plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin;
|
||
}
|
||
|
||
plugin.storage = plugin.opts.storage || tokenStorage;
|
||
}
|
||
|
||
};
|
||
},{"./RequestClient":10,"./tokenStorage":14}],10:[function(require,module,exports){
|
||
'use strict';
|
||
|
||
var _class, _getPostResponseFunc, _getUrl, _errorHandler, _temp;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError');
|
||
|
||
const AuthError = require('./AuthError'); // Remove the trailing slash so we can always safely append /xyz.
|
||
|
||
|
||
function stripSlash(url) {
|
||
return url.replace(/\/$/, '');
|
||
}
|
||
|
||
async function handleJSONResponse(res) {
|
||
if (res.status === 401) {
|
||
throw new AuthError();
|
||
}
|
||
|
||
const jsonPromise = res.json();
|
||
|
||
if (res.status < 200 || res.status > 300) {
|
||
let errMsg = `Failed request with status: ${res.status}. ${res.statusText}`;
|
||
|
||
try {
|
||
const errData = await jsonPromise;
|
||
errMsg = errData.message ? `${errMsg} message: ${errData.message}` : errMsg;
|
||
errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg;
|
||
} finally {
|
||
// eslint-disable-next-line no-unsafe-finally
|
||
throw new Error(errMsg);
|
||
}
|
||
}
|
||
|
||
return jsonPromise;
|
||
}
|
||
|
||
module.exports = (_temp = (_getPostResponseFunc = /*#__PURE__*/_classPrivateFieldLooseKey("getPostResponseFunc"), _getUrl = /*#__PURE__*/_classPrivateFieldLooseKey("getUrl"), _errorHandler = /*#__PURE__*/_classPrivateFieldLooseKey("errorHandler"), _class = class RequestClient {
|
||
// eslint-disable-next-line global-require
|
||
constructor(uppy, opts) {
|
||
Object.defineProperty(this, _errorHandler, {
|
||
value: _errorHandler2
|
||
});
|
||
Object.defineProperty(this, _getUrl, {
|
||
value: _getUrl2
|
||
});
|
||
Object.defineProperty(this, _getPostResponseFunc, {
|
||
writable: true,
|
||
value: skip => response => skip ? response : this.onReceiveResponse(response)
|
||
});
|
||
this.uppy = uppy;
|
||
this.opts = opts;
|
||
this.onReceiveResponse = this.onReceiveResponse.bind(this);
|
||
this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token'];
|
||
this.preflightDone = false;
|
||
}
|
||
|
||
get hostname() {
|
||
const {
|
||
companion
|
||
} = this.uppy.getState();
|
||
const host = this.opts.companionUrl;
|
||
return stripSlash(companion && companion[host] ? companion[host] : host);
|
||
}
|
||
|
||
headers() {
|
||
const userHeaders = this.opts.companionHeaders || {};
|
||
return Promise.resolve({ ...RequestClient.defaultHeaders,
|
||
...userHeaders
|
||
});
|
||
}
|
||
|
||
onReceiveResponse(response) {
|
||
const state = this.uppy.getState();
|
||
const companion = state.companion || {};
|
||
const host = this.opts.companionUrl;
|
||
const {
|
||
headers
|
||
} = response; // Store the self-identified domain name for the Companion instance we just hit.
|
||
|
||
if (headers.has('i-am') && headers.get('i-am') !== companion[host]) {
|
||
this.uppy.setState({
|
||
companion: { ...companion,
|
||
[host]: headers.get('i-am')
|
||
}
|
||
});
|
||
}
|
||
|
||
return response;
|
||
}
|
||
|
||
preflight(path) {
|
||
if (this.preflightDone) {
|
||
return Promise.resolve(this.allowedHeaders.slice());
|
||
}
|
||
|
||
return fetch(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), {
|
||
method: 'OPTIONS'
|
||
}).then(response => {
|
||
if (response.headers.has('access-control-allow-headers')) {
|
||
this.allowedHeaders = response.headers.get('access-control-allow-headers').split(',').map(headerName => headerName.trim().toLowerCase());
|
||
}
|
||
|
||
this.preflightDone = true;
|
||
return this.allowedHeaders.slice();
|
||
}).catch(err => {
|
||
this.uppy.log(`[CompanionClient] unable to make preflight request ${err}`, 'warning');
|
||
this.preflightDone = true;
|
||
return this.allowedHeaders.slice();
|
||
});
|
||
}
|
||
|
||
preflightAndHeaders(path) {
|
||
return Promise.all([this.preflight(path), this.headers()]).then(_ref => {
|
||
let [allowedHeaders, headers] = _ref;
|
||
// filter to keep only allowed Headers
|
||
Object.keys(headers).forEach(header => {
|
||
if (!allowedHeaders.includes(header.toLowerCase())) {
|
||
this.uppy.log(`[CompanionClient] excluding disallowed header ${header}`);
|
||
delete headers[header]; // eslint-disable-line no-param-reassign
|
||
}
|
||
});
|
||
return headers;
|
||
});
|
||
}
|
||
|
||
get(path, skipPostResponse) {
|
||
const method = 'get';
|
||
return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), {
|
||
method,
|
||
headers,
|
||
credentials: this.opts.companionCookiesRule || 'same-origin'
|
||
})).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path));
|
||
}
|
||
|
||
post(path, data, skipPostResponse) {
|
||
const method = 'post';
|
||
return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), {
|
||
method,
|
||
headers,
|
||
credentials: this.opts.companionCookiesRule || 'same-origin',
|
||
body: JSON.stringify(data)
|
||
})).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path));
|
||
}
|
||
|
||
delete(path, data, skipPostResponse) {
|
||
const method = 'delete';
|
||
return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(`${this.hostname}/${path}`, {
|
||
method,
|
||
headers,
|
||
credentials: this.opts.companionCookiesRule || 'same-origin',
|
||
body: data ? JSON.stringify(data) : null
|
||
})).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path));
|
||
}
|
||
|
||
}), _class.VERSION = "2.0.5", _class.defaultHeaders = {
|
||
Accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
'Uppy-Versions': `@uppy/companion-client=${_class.VERSION}`
|
||
}, _temp);
|
||
|
||
function _getUrl2(url) {
|
||
if (/^(https?:|)\/\//.test(url)) {
|
||
return url;
|
||
}
|
||
|
||
return `${this.hostname}/${url}`;
|
||
}
|
||
|
||
function _errorHandler2(method, path) {
|
||
return err => {
|
||
var _err;
|
||
|
||
if (!((_err = err) != null && _err.isAuthError)) {
|
||
const error = new Error(`Could not ${method} ${_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path)}`);
|
||
error.cause = err;
|
||
err = error; // eslint-disable-line no-param-reassign
|
||
}
|
||
|
||
return Promise.reject(err);
|
||
};
|
||
}
|
||
},{"./AuthError":8,"@uppy/utils/lib/fetchWithNetworkError":34}],11:[function(require,module,exports){
|
||
'use strict';
|
||
|
||
const RequestClient = require('./RequestClient');
|
||
|
||
const getName = id => {
|
||
return id.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
|
||
};
|
||
|
||
module.exports = class SearchProvider extends RequestClient {
|
||
constructor(uppy, opts) {
|
||
super(uppy, opts);
|
||
this.provider = opts.provider;
|
||
this.id = this.provider;
|
||
this.name = this.opts.name || getName(this.id);
|
||
this.pluginId = this.opts.pluginId;
|
||
}
|
||
|
||
fileUrl(id) {
|
||
return `${this.hostname}/search/${this.id}/get/${id}`;
|
||
}
|
||
|
||
search(text, queries) {
|
||
queries = queries ? `&${queries}` : '';
|
||
return this.get(`search/${this.id}/list?q=${encodeURIComponent(text)}${queries}`);
|
||
}
|
||
|
||
};
|
||
},{"./RequestClient":10}],12:[function(require,module,exports){
|
||
var _queued, _emitter, _isOpen, _socket, _handleMessage;
|
||
|
||
let _Symbol$for, _Symbol$for2;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const ee = require('namespace-emitter');
|
||
|
||
module.exports = (_queued = /*#__PURE__*/_classPrivateFieldLooseKey("queued"), _emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter"), _isOpen = /*#__PURE__*/_classPrivateFieldLooseKey("isOpen"), _socket = /*#__PURE__*/_classPrivateFieldLooseKey("socket"), _handleMessage = /*#__PURE__*/_classPrivateFieldLooseKey("handleMessage"), _Symbol$for = Symbol.for('uppy test: getSocket'), _Symbol$for2 = Symbol.for('uppy test: getQueued'), class UppySocket {
|
||
constructor(opts) {
|
||
Object.defineProperty(this, _queued, {
|
||
writable: true,
|
||
value: []
|
||
});
|
||
Object.defineProperty(this, _emitter, {
|
||
writable: true,
|
||
value: ee()
|
||
});
|
||
Object.defineProperty(this, _isOpen, {
|
||
writable: true,
|
||
value: false
|
||
});
|
||
Object.defineProperty(this, _socket, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _handleMessage, {
|
||
writable: true,
|
||
value: e => {
|
||
try {
|
||
const message = JSON.parse(e.data);
|
||
this.emit(message.action, message.payload);
|
||
} catch (err) {
|
||
// TODO: use a more robust error handler.
|
||
console.log(err); // eslint-disable-line no-console
|
||
}
|
||
}
|
||
});
|
||
this.opts = opts;
|
||
|
||
if (!opts || opts.autoOpen !== false) {
|
||
this.open();
|
||
}
|
||
}
|
||
|
||
get isOpen() {
|
||
return _classPrivateFieldLooseBase(this, _isOpen)[_isOpen];
|
||
}
|
||
|
||
[_Symbol$for]() {
|
||
return _classPrivateFieldLooseBase(this, _socket)[_socket];
|
||
}
|
||
|
||
[_Symbol$for2]() {
|
||
return _classPrivateFieldLooseBase(this, _queued)[_queued];
|
||
}
|
||
|
||
open() {
|
||
_classPrivateFieldLooseBase(this, _socket)[_socket] = new WebSocket(this.opts.target);
|
||
|
||
_classPrivateFieldLooseBase(this, _socket)[_socket].onopen = () => {
|
||
_classPrivateFieldLooseBase(this, _isOpen)[_isOpen] = true;
|
||
|
||
while (_classPrivateFieldLooseBase(this, _queued)[_queued].length > 0 && _classPrivateFieldLooseBase(this, _isOpen)[_isOpen]) {
|
||
const first = _classPrivateFieldLooseBase(this, _queued)[_queued].shift();
|
||
|
||
this.send(first.action, first.payload);
|
||
}
|
||
};
|
||
|
||
_classPrivateFieldLooseBase(this, _socket)[_socket].onclose = () => {
|
||
_classPrivateFieldLooseBase(this, _isOpen)[_isOpen] = false;
|
||
};
|
||
|
||
_classPrivateFieldLooseBase(this, _socket)[_socket].onmessage = _classPrivateFieldLooseBase(this, _handleMessage)[_handleMessage];
|
||
}
|
||
|
||
close() {
|
||
var _classPrivateFieldLoo;
|
||
|
||
(_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _socket)[_socket]) == null ? void 0 : _classPrivateFieldLoo.close();
|
||
}
|
||
|
||
send(action, payload) {
|
||
// attach uuid
|
||
if (!_classPrivateFieldLooseBase(this, _isOpen)[_isOpen]) {
|
||
_classPrivateFieldLooseBase(this, _queued)[_queued].push({
|
||
action,
|
||
payload
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _socket)[_socket].send(JSON.stringify({
|
||
action,
|
||
payload
|
||
}));
|
||
}
|
||
|
||
on(action, handler) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].on(action, handler);
|
||
}
|
||
|
||
emit(action, payload) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].emit(action, payload);
|
||
}
|
||
|
||
once(action, handler) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].once(action, handler);
|
||
}
|
||
|
||
});
|
||
},{"namespace-emitter":56}],13:[function(require,module,exports){
|
||
'use strict';
|
||
/**
|
||
* Manages communications with Companion
|
||
*/
|
||
|
||
const RequestClient = require('./RequestClient');
|
||
|
||
const Provider = require('./Provider');
|
||
|
||
const SearchProvider = require('./SearchProvider');
|
||
|
||
const Socket = require('./Socket');
|
||
|
||
module.exports = {
|
||
RequestClient,
|
||
Provider,
|
||
SearchProvider,
|
||
Socket
|
||
};
|
||
},{"./Provider":9,"./RequestClient":10,"./SearchProvider":11,"./Socket":12}],14:[function(require,module,exports){
|
||
'use strict';
|
||
/**
|
||
* This module serves as an Async wrapper for LocalStorage
|
||
*/
|
||
|
||
module.exports.setItem = (key, value) => {
|
||
return new Promise(resolve => {
|
||
localStorage.setItem(key, value);
|
||
resolve();
|
||
});
|
||
};
|
||
|
||
module.exports.getItem = key => {
|
||
return Promise.resolve(localStorage.getItem(key));
|
||
};
|
||
|
||
module.exports.removeItem = key => {
|
||
return new Promise(resolve => {
|
||
localStorage.removeItem(key);
|
||
resolve();
|
||
});
|
||
};
|
||
},{}],15:[function(require,module,exports){
|
||
/**
|
||
* Core plugin logic that all plugins share.
|
||
*
|
||
* BasePlugin does not contain DOM rendering so it can be used for plugins
|
||
* without a user interface.
|
||
*
|
||
* See `Plugin` for the extended version with Preact rendering for interfaces.
|
||
*/
|
||
const Translator = require('@uppy/utils/lib/Translator');
|
||
|
||
module.exports = class BasePlugin {
|
||
constructor(uppy, opts) {
|
||
if (opts === void 0) {
|
||
opts = {};
|
||
}
|
||
|
||
this.uppy = uppy;
|
||
this.opts = opts;
|
||
}
|
||
|
||
getPluginState() {
|
||
const {
|
||
plugins
|
||
} = this.uppy.getState();
|
||
return plugins[this.id] || {};
|
||
}
|
||
|
||
setPluginState(update) {
|
||
const {
|
||
plugins
|
||
} = this.uppy.getState();
|
||
this.uppy.setState({
|
||
plugins: { ...plugins,
|
||
[this.id]: { ...plugins[this.id],
|
||
...update
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
setOptions(newOpts) {
|
||
this.opts = { ...this.opts,
|
||
...newOpts
|
||
};
|
||
this.setPluginState(); // so that UI re-renders with new options
|
||
|
||
this.i18nInit();
|
||
}
|
||
|
||
i18nInit() {
|
||
const translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]);
|
||
this.i18n = translator.translate.bind(translator);
|
||
this.i18nArray = translator.translateArray.bind(translator);
|
||
this.setPluginState(); // so that UI re-renders and we see the updated locale
|
||
}
|
||
/**
|
||
* Extendable methods
|
||
* ==================
|
||
* These methods are here to serve as an overview of the extendable methods as well as
|
||
* making them not conditional in use, such as `if (this.afterUpdate)`.
|
||
*/
|
||
// eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
addTarget() {
|
||
throw new Error('Extend the addTarget method to add your plugin to another plugin\'s target');
|
||
} // eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
install() {} // eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
uninstall() {}
|
||
/**
|
||
* Called when plugin is mounted, whether in DOM or into another plugin.
|
||
* Needed because sometimes plugins are mounted separately/after `install`,
|
||
* so this.el and this.parent might not be available in `install`.
|
||
* This is the case with @uppy/react plugins, for example.
|
||
*/
|
||
|
||
|
||
render() {
|
||
throw new Error('Extend the render method to add your plugin to a DOM element');
|
||
} // eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
update() {} // Called after every state update, after everything's mounted. Debounced.
|
||
// eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
afterUpdate() {}
|
||
|
||
};
|
||
},{"@uppy/utils/lib/Translator":31}],16:[function(require,module,exports){
|
||
/* eslint-disable max-classes-per-file, class-methods-use-this */
|
||
|
||
/* global AggregateError */
|
||
const prettierBytes = require('@transloadit/prettier-bytes');
|
||
|
||
const match = require('mime-match');
|
||
|
||
const defaultOptions = {
|
||
maxFileSize: null,
|
||
minFileSize: null,
|
||
maxTotalFileSize: null,
|
||
maxNumberOfFiles: null,
|
||
minNumberOfFiles: null,
|
||
allowedFileTypes: null,
|
||
requiredMetaFields: []
|
||
};
|
||
|
||
class RestrictionError extends Error {
|
||
constructor() {
|
||
super(...arguments);
|
||
this.isRestriction = true;
|
||
}
|
||
|
||
}
|
||
|
||
if (typeof AggregateError === 'undefined') {
|
||
// eslint-disable-next-line no-global-assign
|
||
// TODO: remove this "polyfill" in the next major.
|
||
globalThis.AggregateError = class AggregateError extends Error {
|
||
constructor(errors, message) {
|
||
super(message);
|
||
this.errors = errors;
|
||
}
|
||
|
||
};
|
||
}
|
||
|
||
class Restricter {
|
||
constructor(getOpts, i18n) {
|
||
this.i18n = i18n;
|
||
|
||
this.getOpts = () => {
|
||
const opts = getOpts();
|
||
|
||
if (opts.restrictions.allowedFileTypes != null && !Array.isArray(opts.restrictions.allowedFileTypes)) {
|
||
throw new TypeError('`restrictions.allowedFileTypes` must be an array');
|
||
}
|
||
|
||
return opts;
|
||
};
|
||
}
|
||
|
||
validate(file, files) {
|
||
const {
|
||
maxFileSize,
|
||
minFileSize,
|
||
maxTotalFileSize,
|
||
maxNumberOfFiles,
|
||
allowedFileTypes
|
||
} = this.getOpts().restrictions;
|
||
|
||
if (maxNumberOfFiles && files.length + 1 > maxNumberOfFiles) {
|
||
throw new RestrictionError(`${this.i18n('youCanOnlyUploadX', {
|
||
smart_count: maxNumberOfFiles
|
||
})}`);
|
||
}
|
||
|
||
if (allowedFileTypes) {
|
||
const isCorrectFileType = allowedFileTypes.some(type => {
|
||
// check if this is a mime-type
|
||
if (type.includes('/')) {
|
||
if (!file.type) return false;
|
||
return match(file.type.replace(/;.*?$/, ''), type);
|
||
} // otherwise this is likely an extension
|
||
|
||
|
||
if (type[0] === '.' && file.extension) {
|
||
return file.extension.toLowerCase() === type.substr(1).toLowerCase();
|
||
}
|
||
|
||
return false;
|
||
});
|
||
|
||
if (!isCorrectFileType) {
|
||
const allowedFileTypesString = allowedFileTypes.join(', ');
|
||
throw new RestrictionError(this.i18n('youCanOnlyUploadFileTypes', {
|
||
types: allowedFileTypesString
|
||
}));
|
||
}
|
||
} // We can't check maxTotalFileSize if the size is unknown.
|
||
|
||
|
||
if (maxTotalFileSize && file.size != null) {
|
||
const totalFilesSize = files.reduce((total, f) => total + f.size, file.size);
|
||
|
||
if (totalFilesSize > maxTotalFileSize) {
|
||
throw new RestrictionError(this.i18n('exceedsSize', {
|
||
size: prettierBytes(maxTotalFileSize),
|
||
file: file.name
|
||
}));
|
||
}
|
||
} // We can't check maxFileSize if the size is unknown.
|
||
|
||
|
||
if (maxFileSize && file.size != null && file.size > maxFileSize) {
|
||
throw new RestrictionError(this.i18n('exceedsSize', {
|
||
size: prettierBytes(maxFileSize),
|
||
file: file.name
|
||
}));
|
||
} // We can't check minFileSize if the size is unknown.
|
||
|
||
|
||
if (minFileSize && file.size != null && file.size < minFileSize) {
|
||
throw new RestrictionError(this.i18n('inferiorSize', {
|
||
size: prettierBytes(minFileSize)
|
||
}));
|
||
}
|
||
}
|
||
|
||
validateMinNumberOfFiles(files) {
|
||
const {
|
||
minNumberOfFiles
|
||
} = this.getOpts().restrictions;
|
||
|
||
if (Object.keys(files).length < minNumberOfFiles) {
|
||
throw new RestrictionError(this.i18n('youHaveToAtLeastSelectX', {
|
||
smart_count: minNumberOfFiles
|
||
}));
|
||
}
|
||
}
|
||
|
||
getMissingRequiredMetaFields(file) {
|
||
const error = new RestrictionError(this.i18n('missingRequiredMetaFieldOnFile', {
|
||
fileName: file.name
|
||
}));
|
||
const {
|
||
requiredMetaFields
|
||
} = this.getOpts().restrictions; // TODO: migrate to Object.hasOwn in the next major.
|
||
|
||
const own = Object.prototype.hasOwnProperty;
|
||
const missingFields = [];
|
||
|
||
for (const field of requiredMetaFields) {
|
||
if (!own.call(file.meta, field) || file.meta[field] === '') {
|
||
missingFields.push(field);
|
||
}
|
||
}
|
||
|
||
return {
|
||
missingFields,
|
||
error
|
||
};
|
||
}
|
||
|
||
}
|
||
|
||
module.exports = {
|
||
Restricter,
|
||
defaultOptions,
|
||
RestrictionError
|
||
};
|
||
},{"@transloadit/prettier-bytes":1,"mime-match":55}],17:[function(require,module,exports){
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const {
|
||
render
|
||
} = require('preact');
|
||
|
||
const findDOMElement = require('@uppy/utils/lib/findDOMElement');
|
||
|
||
const BasePlugin = require('./BasePlugin');
|
||
/**
|
||
* Defer a frequent call to the microtask queue.
|
||
*
|
||
* @param {() => T} fn
|
||
* @returns {Promise<T>}
|
||
*/
|
||
|
||
|
||
function debounce(fn) {
|
||
let calling = null;
|
||
let latestArgs = null;
|
||
return function () {
|
||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
args[_key] = arguments[_key];
|
||
}
|
||
|
||
latestArgs = args;
|
||
|
||
if (!calling) {
|
||
calling = Promise.resolve().then(() => {
|
||
calling = null; // At this point `args` may be different from the most
|
||
// recent state, if multiple calls happened since this task
|
||
// was queued. So we use the `latestArgs`, which definitely
|
||
// is the most recent call.
|
||
|
||
return fn(...latestArgs);
|
||
});
|
||
}
|
||
|
||
return calling;
|
||
};
|
||
}
|
||
/**
|
||
* UIPlugin is the extended version of BasePlugin to incorporate rendering with Preact.
|
||
* Use this for plugins that need a user interface.
|
||
*
|
||
* For plugins without an user interface, see BasePlugin.
|
||
*/
|
||
|
||
|
||
var _updateUI = /*#__PURE__*/_classPrivateFieldLooseKey("updateUI");
|
||
|
||
class UIPlugin extends BasePlugin {
|
||
constructor() {
|
||
super(...arguments);
|
||
Object.defineProperty(this, _updateUI, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Check if supplied `target` is a DOM element or an `object`.
|
||
* If it’s an object — target is a plugin, and we search `plugins`
|
||
* for a plugin with same name and return its target.
|
||
*/
|
||
mount(target, plugin) {
|
||
const callerPluginName = plugin.id;
|
||
const targetElement = findDOMElement(target);
|
||
|
||
if (targetElement) {
|
||
this.isTargetDOMEl = true; // When target is <body> with a single <div> element,
|
||
// Preact thinks it’s the Uppy root element in there when doing a diff,
|
||
// and destroys it. So we are creating a fragment (could be empty div)
|
||
|
||
const uppyRootElement = document.createDocumentFragment(); // API for plugins that require a synchronous rerender.
|
||
|
||
_classPrivateFieldLooseBase(this, _updateUI)[_updateUI] = debounce(state => {
|
||
// plugin could be removed, but this.rerender is debounced below,
|
||
// so it could still be called even after uppy.removePlugin or uppy.close
|
||
// hence the check
|
||
if (!this.uppy.getPlugin(this.id)) return;
|
||
render(this.render(state), uppyRootElement);
|
||
this.afterUpdate();
|
||
});
|
||
this.uppy.log(`Installing ${callerPluginName} to a DOM element '${target}'`);
|
||
|
||
if (this.opts.replaceTargetContent) {
|
||
// Doing render(h(null), targetElement), which should have been
|
||
// a better way, since because the component might need to do additional cleanup when it is removed,
|
||
// stopped working — Preact just adds null into target, not replacing
|
||
targetElement.innerHTML = '';
|
||
}
|
||
|
||
render(this.render(this.uppy.getState()), uppyRootElement);
|
||
this.el = uppyRootElement.firstElementChild;
|
||
targetElement.appendChild(uppyRootElement);
|
||
this.onMount();
|
||
return this.el;
|
||
}
|
||
|
||
let targetPlugin;
|
||
|
||
if (typeof target === 'object' && target instanceof UIPlugin) {
|
||
// Targeting a plugin *instance*
|
||
targetPlugin = target;
|
||
} else if (typeof target === 'function') {
|
||
// Targeting a plugin type
|
||
const Target = target; // Find the target plugin instance.
|
||
|
||
this.uppy.iteratePlugins(p => {
|
||
if (p instanceof Target) {
|
||
targetPlugin = p;
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
|
||
if (targetPlugin) {
|
||
this.uppy.log(`Installing ${callerPluginName} to ${targetPlugin.id}`);
|
||
this.parent = targetPlugin;
|
||
this.el = targetPlugin.addTarget(plugin);
|
||
this.onMount();
|
||
return this.el;
|
||
}
|
||
|
||
this.uppy.log(`Not installing ${callerPluginName}`);
|
||
let message = `Invalid target option given to ${callerPluginName}.`;
|
||
|
||
if (typeof target === 'function') {
|
||
message += ' The given target is not a Plugin class. ' + 'Please check that you\'re not specifying a React Component instead of a plugin. ' + 'If you are using @uppy/* packages directly, make sure you have only 1 version of @uppy/core installed: ' + 'run `npm ls @uppy/core` on the command line and verify that all the versions match and are deduped correctly.';
|
||
} else {
|
||
message += 'If you meant to target an HTML element, please make sure that the element exists. ' + 'Check that the <script> tag initializing Uppy is right before the closing </body> tag at the end of the page. ' + '(see https://github.com/transloadit/uppy/issues/1042)\n\n' + 'If you meant to target a plugin, please confirm that your `import` statements or `require` calls are correct.';
|
||
}
|
||
|
||
throw new Error(message);
|
||
}
|
||
|
||
update(state) {
|
||
if (this.el != null) {
|
||
var _classPrivateFieldLoo, _classPrivateFieldLoo2;
|
||
|
||
(_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(this, _updateUI))[_updateUI]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2, state);
|
||
}
|
||
}
|
||
|
||
unmount() {
|
||
if (this.isTargetDOMEl) {
|
||
var _this$el;
|
||
|
||
(_this$el = this.el) == null ? void 0 : _this$el.remove();
|
||
}
|
||
|
||
this.onUnmount();
|
||
} // eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
onMount() {} // eslint-disable-next-line class-methods-use-this
|
||
|
||
|
||
onUnmount() {}
|
||
|
||
}
|
||
|
||
module.exports = UIPlugin;
|
||
},{"./BasePlugin":15,"@uppy/utils/lib/findDOMElement":35,"preact":58}],18:[function(require,module,exports){
|
||
/* eslint-disable max-classes-per-file */
|
||
|
||
/* global AggregateError */
|
||
'use strict';
|
||
|
||
let _Symbol$for, _Symbol$for2;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const Translator = require('@uppy/utils/lib/Translator');
|
||
|
||
const ee = require('namespace-emitter');
|
||
|
||
const {
|
||
nanoid
|
||
} = require('nanoid/non-secure');
|
||
|
||
const throttle = require('lodash.throttle');
|
||
|
||
const DefaultStore = require('@uppy/store-default');
|
||
|
||
const getFileType = require('@uppy/utils/lib/getFileType');
|
||
|
||
const getFileNameAndExtension = require('@uppy/utils/lib/getFileNameAndExtension');
|
||
|
||
const generateFileID = require('@uppy/utils/lib/generateFileID');
|
||
|
||
const supportsUploadProgress = require('./supportsUploadProgress');
|
||
|
||
const getFileName = require('./getFileName');
|
||
|
||
const {
|
||
justErrorsLogger,
|
||
debugLogger
|
||
} = require('./loggers');
|
||
|
||
const {
|
||
Restricter,
|
||
defaultOptions: defaultRestrictionOptions,
|
||
RestrictionError
|
||
} = require('./Restricter');
|
||
|
||
const locale = require('./locale'); // Exported from here.
|
||
|
||
/**
|
||
* Uppy Core module.
|
||
* Manages plugins, state updates, acts as an event bus,
|
||
* adds/removes files and metadata.
|
||
*/
|
||
|
||
|
||
var _plugins = /*#__PURE__*/_classPrivateFieldLooseKey("plugins");
|
||
|
||
var _restricter = /*#__PURE__*/_classPrivateFieldLooseKey("restricter");
|
||
|
||
var _storeUnsubscribe = /*#__PURE__*/_classPrivateFieldLooseKey("storeUnsubscribe");
|
||
|
||
var _emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter");
|
||
|
||
var _preProcessors = /*#__PURE__*/_classPrivateFieldLooseKey("preProcessors");
|
||
|
||
var _uploaders = /*#__PURE__*/_classPrivateFieldLooseKey("uploaders");
|
||
|
||
var _postProcessors = /*#__PURE__*/_classPrivateFieldLooseKey("postProcessors");
|
||
|
||
var _informAndEmit = /*#__PURE__*/_classPrivateFieldLooseKey("informAndEmit");
|
||
|
||
var _checkRequiredMetaFieldsOnFile = /*#__PURE__*/_classPrivateFieldLooseKey("checkRequiredMetaFieldsOnFile");
|
||
|
||
var _checkRequiredMetaFields = /*#__PURE__*/_classPrivateFieldLooseKey("checkRequiredMetaFields");
|
||
|
||
var _assertNewUploadAllowed = /*#__PURE__*/_classPrivateFieldLooseKey("assertNewUploadAllowed");
|
||
|
||
var _checkAndCreateFileStateObject = /*#__PURE__*/_classPrivateFieldLooseKey("checkAndCreateFileStateObject");
|
||
|
||
var _startIfAutoProceed = /*#__PURE__*/_classPrivateFieldLooseKey("startIfAutoProceed");
|
||
|
||
var _addListeners = /*#__PURE__*/_classPrivateFieldLooseKey("addListeners");
|
||
|
||
var _updateOnlineStatus = /*#__PURE__*/_classPrivateFieldLooseKey("updateOnlineStatus");
|
||
|
||
var _createUpload = /*#__PURE__*/_classPrivateFieldLooseKey("createUpload");
|
||
|
||
var _getUpload = /*#__PURE__*/_classPrivateFieldLooseKey("getUpload");
|
||
|
||
var _removeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("removeUpload");
|
||
|
||
var _runUpload = /*#__PURE__*/_classPrivateFieldLooseKey("runUpload");
|
||
|
||
_Symbol$for = Symbol.for('uppy test: getPlugins');
|
||
_Symbol$for2 = Symbol.for('uppy test: createUpload');
|
||
|
||
class Uppy {
|
||
// eslint-disable-next-line global-require
|
||
|
||
/** @type {Record<string, BasePlugin[]>} */
|
||
|
||
/**
|
||
* Instantiate Uppy
|
||
*
|
||
* @param {object} opts — Uppy options
|
||
*/
|
||
constructor(_opts) {
|
||
Object.defineProperty(this, _runUpload, {
|
||
value: _runUpload2
|
||
});
|
||
Object.defineProperty(this, _removeUpload, {
|
||
value: _removeUpload2
|
||
});
|
||
Object.defineProperty(this, _getUpload, {
|
||
value: _getUpload2
|
||
});
|
||
Object.defineProperty(this, _createUpload, {
|
||
value: _createUpload2
|
||
});
|
||
Object.defineProperty(this, _addListeners, {
|
||
value: _addListeners2
|
||
});
|
||
Object.defineProperty(this, _startIfAutoProceed, {
|
||
value: _startIfAutoProceed2
|
||
});
|
||
Object.defineProperty(this, _checkAndCreateFileStateObject, {
|
||
value: _checkAndCreateFileStateObject2
|
||
});
|
||
Object.defineProperty(this, _assertNewUploadAllowed, {
|
||
value: _assertNewUploadAllowed2
|
||
});
|
||
Object.defineProperty(this, _checkRequiredMetaFields, {
|
||
value: _checkRequiredMetaFields2
|
||
});
|
||
Object.defineProperty(this, _checkRequiredMetaFieldsOnFile, {
|
||
value: _checkRequiredMetaFieldsOnFile2
|
||
});
|
||
Object.defineProperty(this, _informAndEmit, {
|
||
value: _informAndEmit2
|
||
});
|
||
Object.defineProperty(this, _plugins, {
|
||
writable: true,
|
||
value: Object.create(null)
|
||
});
|
||
Object.defineProperty(this, _restricter, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _storeUnsubscribe, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _emitter, {
|
||
writable: true,
|
||
value: ee()
|
||
});
|
||
Object.defineProperty(this, _preProcessors, {
|
||
writable: true,
|
||
value: new Set()
|
||
});
|
||
Object.defineProperty(this, _uploaders, {
|
||
writable: true,
|
||
value: new Set()
|
||
});
|
||
Object.defineProperty(this, _postProcessors, {
|
||
writable: true,
|
||
value: new Set()
|
||
});
|
||
Object.defineProperty(this, _updateOnlineStatus, {
|
||
writable: true,
|
||
value: this.updateOnlineStatus.bind(this)
|
||
});
|
||
this.defaultLocale = locale;
|
||
const defaultOptions = {
|
||
id: 'uppy',
|
||
autoProceed: false,
|
||
|
||
/**
|
||
* @deprecated The method should not be used
|
||
*/
|
||
allowMultipleUploads: true,
|
||
allowMultipleUploadBatches: true,
|
||
debug: false,
|
||
restrictions: defaultRestrictionOptions,
|
||
meta: {},
|
||
onBeforeFileAdded: currentFile => currentFile,
|
||
onBeforeUpload: files => files,
|
||
store: DefaultStore(),
|
||
logger: justErrorsLogger,
|
||
infoTimeout: 5000
|
||
}; // Merge default options with the ones set by user,
|
||
// making sure to merge restrictions too
|
||
|
||
this.opts = { ...defaultOptions,
|
||
..._opts,
|
||
restrictions: { ...defaultOptions.restrictions,
|
||
...(_opts && _opts.restrictions)
|
||
}
|
||
}; // Support debug: true for backwards-compatability, unless logger is set in opts
|
||
// opts instead of this.opts to avoid comparing objects — we set logger: justErrorsLogger in defaultOptions
|
||
|
||
if (_opts && _opts.logger && _opts.debug) {
|
||
this.log('You are using a custom `logger`, but also set `debug: true`, which uses built-in logger to output logs to console. Ignoring `debug: true` and using your custom `logger`.', 'warning');
|
||
} else if (_opts && _opts.debug) {
|
||
this.opts.logger = debugLogger;
|
||
}
|
||
|
||
this.log(`Using Core v${this.constructor.VERSION}`);
|
||
this.i18nInit(); // ___Why throttle at 500ms?
|
||
// - We must throttle at >250ms for superfocus in Dashboard to work well
|
||
// (because animation takes 0.25s, and we want to wait for all animations to be over before refocusing).
|
||
// [Practical Check]: if thottle is at 100ms, then if you are uploading a file,
|
||
// and click 'ADD MORE FILES', - focus won't activate in Firefox.
|
||
// - We must throttle at around >500ms to avoid performance lags.
|
||
// [Practical Check] Firefox, try to upload a big file for a prolonged period of time. Laptop will start to heat up.
|
||
|
||
this.calculateProgress = throttle(this.calculateProgress.bind(this), 500, {
|
||
leading: true,
|
||
trailing: true
|
||
});
|
||
this.store = this.opts.store;
|
||
this.setState({
|
||
plugins: {},
|
||
files: {},
|
||
currentUploads: {},
|
||
allowNewUpload: true,
|
||
capabilities: {
|
||
uploadProgress: supportsUploadProgress(),
|
||
individualCancellation: true,
|
||
resumableUploads: false
|
||
},
|
||
totalProgress: 0,
|
||
meta: { ...this.opts.meta
|
||
},
|
||
info: [],
|
||
recoveredState: null
|
||
});
|
||
_classPrivateFieldLooseBase(this, _restricter)[_restricter] = new Restricter(() => this.opts, this.i18n);
|
||
_classPrivateFieldLooseBase(this, _storeUnsubscribe)[_storeUnsubscribe] = this.store.subscribe((prevState, nextState, patch) => {
|
||
this.emit('state-update', prevState, nextState, patch);
|
||
this.updateAll(nextState);
|
||
}); // Exposing uppy object on window for debugging and testing
|
||
|
||
if (this.opts.debug && typeof window !== 'undefined') {
|
||
window[this.opts.id] = this;
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _addListeners)[_addListeners]();
|
||
}
|
||
|
||
emit(event) {
|
||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||
args[_key - 1] = arguments[_key];
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].emit(event, ...args);
|
||
}
|
||
|
||
on(event, callback) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].on(event, callback);
|
||
|
||
return this;
|
||
}
|
||
|
||
once(event, callback) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].once(event, callback);
|
||
|
||
return this;
|
||
}
|
||
|
||
off(event, callback) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].off(event, callback);
|
||
|
||
return this;
|
||
}
|
||
/**
|
||
* Iterate on all plugins and run `update` on them.
|
||
* Called each time state changes.
|
||
*
|
||
*/
|
||
|
||
|
||
updateAll(state) {
|
||
this.iteratePlugins(plugin => {
|
||
plugin.update(state);
|
||
});
|
||
}
|
||
/**
|
||
* Updates state with a patch
|
||
*
|
||
* @param {object} patch {foo: 'bar'}
|
||
*/
|
||
|
||
|
||
setState(patch) {
|
||
this.store.setState(patch);
|
||
}
|
||
/**
|
||
* Returns current state.
|
||
*
|
||
* @returns {object}
|
||
*/
|
||
|
||
|
||
getState() {
|
||
return this.store.getState();
|
||
}
|
||
/**
|
||
* Back compat for when uppy.state is used instead of uppy.getState().
|
||
*
|
||
* @deprecated
|
||
*/
|
||
|
||
|
||
get state() {
|
||
// Here, state is a non-enumerable property.
|
||
return this.getState();
|
||
}
|
||
/**
|
||
* Shorthand to set state for a specific file.
|
||
*/
|
||
|
||
|
||
setFileState(fileID, state) {
|
||
if (!this.getState().files[fileID]) {
|
||
throw new Error(`Can’t set state for ${fileID} (the file could have been removed)`);
|
||
}
|
||
|
||
this.setState({
|
||
files: { ...this.getState().files,
|
||
[fileID]: { ...this.getState().files[fileID],
|
||
...state
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
i18nInit() {
|
||
const translator = new Translator([this.defaultLocale, this.opts.locale]);
|
||
this.i18n = translator.translate.bind(translator);
|
||
this.i18nArray = translator.translateArray.bind(translator);
|
||
this.locale = translator.locale;
|
||
}
|
||
|
||
setOptions(newOpts) {
|
||
this.opts = { ...this.opts,
|
||
...newOpts,
|
||
restrictions: { ...this.opts.restrictions,
|
||
...(newOpts && newOpts.restrictions)
|
||
}
|
||
};
|
||
|
||
if (newOpts.meta) {
|
||
this.setMeta(newOpts.meta);
|
||
}
|
||
|
||
this.i18nInit();
|
||
|
||
if (newOpts.locale) {
|
||
this.iteratePlugins(plugin => {
|
||
plugin.setOptions();
|
||
});
|
||
} // Note: this is not the preact `setState`, it's an internal function that has the same name.
|
||
|
||
|
||
this.setState(); // so that UI re-renders with new options
|
||
}
|
||
|
||
resetProgress() {
|
||
const defaultProgress = {
|
||
percentage: 0,
|
||
bytesUploaded: 0,
|
||
uploadComplete: false,
|
||
uploadStarted: null
|
||
};
|
||
const files = { ...this.getState().files
|
||
};
|
||
const updatedFiles = {};
|
||
Object.keys(files).forEach(fileID => {
|
||
const updatedFile = { ...files[fileID]
|
||
};
|
||
updatedFile.progress = { ...updatedFile.progress,
|
||
...defaultProgress
|
||
};
|
||
updatedFiles[fileID] = updatedFile;
|
||
});
|
||
this.setState({
|
||
files: updatedFiles,
|
||
totalProgress: 0
|
||
});
|
||
this.emit('reset-progress');
|
||
}
|
||
|
||
addPreProcessor(fn) {
|
||
_classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors].add(fn);
|
||
}
|
||
|
||
removePreProcessor(fn) {
|
||
return _classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors].delete(fn);
|
||
}
|
||
|
||
addPostProcessor(fn) {
|
||
_classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].add(fn);
|
||
}
|
||
|
||
removePostProcessor(fn) {
|
||
return _classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].delete(fn);
|
||
}
|
||
|
||
addUploader(fn) {
|
||
_classPrivateFieldLooseBase(this, _uploaders)[_uploaders].add(fn);
|
||
}
|
||
|
||
removeUploader(fn) {
|
||
return _classPrivateFieldLooseBase(this, _uploaders)[_uploaders].delete(fn);
|
||
}
|
||
|
||
setMeta(data) {
|
||
const updatedMeta = { ...this.getState().meta,
|
||
...data
|
||
};
|
||
const updatedFiles = { ...this.getState().files
|
||
};
|
||
Object.keys(updatedFiles).forEach(fileID => {
|
||
updatedFiles[fileID] = { ...updatedFiles[fileID],
|
||
meta: { ...updatedFiles[fileID].meta,
|
||
...data
|
||
}
|
||
};
|
||
});
|
||
this.log('Adding metadata:');
|
||
this.log(data);
|
||
this.setState({
|
||
meta: updatedMeta,
|
||
files: updatedFiles
|
||
});
|
||
}
|
||
|
||
setFileMeta(fileID, data) {
|
||
const updatedFiles = { ...this.getState().files
|
||
};
|
||
|
||
if (!updatedFiles[fileID]) {
|
||
this.log('Was trying to set metadata for a file that has been removed: ', fileID);
|
||
return;
|
||
}
|
||
|
||
const newMeta = { ...updatedFiles[fileID].meta,
|
||
...data
|
||
};
|
||
updatedFiles[fileID] = { ...updatedFiles[fileID],
|
||
meta: newMeta
|
||
};
|
||
this.setState({
|
||
files: updatedFiles
|
||
});
|
||
}
|
||
/**
|
||
* Get a file object.
|
||
*
|
||
* @param {string} fileID The ID of the file object to return.
|
||
*/
|
||
|
||
|
||
getFile(fileID) {
|
||
return this.getState().files[fileID];
|
||
}
|
||
/**
|
||
* Get all files in an array.
|
||
*/
|
||
|
||
|
||
getFiles() {
|
||
const {
|
||
files
|
||
} = this.getState();
|
||
return Object.values(files);
|
||
}
|
||
|
||
getObjectOfFilesPerState() {
|
||
const {
|
||
files: filesObject,
|
||
totalProgress,
|
||
error
|
||
} = this.getState();
|
||
const files = Object.values(filesObject);
|
||
const inProgressFiles = files.filter(_ref => {
|
||
let {
|
||
progress
|
||
} = _ref;
|
||
return !progress.uploadComplete && progress.uploadStarted;
|
||
});
|
||
const newFiles = files.filter(file => !file.progress.uploadStarted);
|
||
const startedFiles = files.filter(file => file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess);
|
||
const uploadStartedFiles = files.filter(file => file.progress.uploadStarted);
|
||
const pausedFiles = files.filter(file => file.isPaused);
|
||
const completeFiles = files.filter(file => file.progress.uploadComplete);
|
||
const erroredFiles = files.filter(file => file.error);
|
||
const inProgressNotPausedFiles = inProgressFiles.filter(file => !file.isPaused);
|
||
const processingFiles = files.filter(file => file.progress.preprocess || file.progress.postprocess);
|
||
return {
|
||
newFiles,
|
||
startedFiles,
|
||
uploadStartedFiles,
|
||
pausedFiles,
|
||
completeFiles,
|
||
erroredFiles,
|
||
inProgressFiles,
|
||
inProgressNotPausedFiles,
|
||
processingFiles,
|
||
isUploadStarted: uploadStartedFiles.length > 0,
|
||
isAllComplete: totalProgress === 100 && completeFiles.length === files.length && processingFiles.length === 0,
|
||
isAllErrored: !!error && erroredFiles.length === files.length,
|
||
isAllPaused: inProgressFiles.length !== 0 && pausedFiles.length === inProgressFiles.length,
|
||
isUploadInProgress: inProgressFiles.length > 0,
|
||
isSomeGhost: files.some(file => file.isGhost)
|
||
};
|
||
}
|
||
/*
|
||
* @constructs
|
||
* @param { Error } error
|
||
* @param { undefined } file
|
||
*/
|
||
|
||
/*
|
||
* @constructs
|
||
* @param { RestrictionError } error
|
||
* @param { UppyFile | undefined } file
|
||
*/
|
||
|
||
|
||
validateRestrictions(file, files) {
|
||
if (files === void 0) {
|
||
files = this.getFiles();
|
||
}
|
||
|
||
// TODO: directly return the Restriction error in next major version.
|
||
// we create RestrictionError's just to discard immediately, which doesn't make sense.
|
||
try {
|
||
_classPrivateFieldLooseBase(this, _restricter)[_restricter].validate(file, files);
|
||
|
||
return {
|
||
result: true
|
||
};
|
||
} catch (err) {
|
||
return {
|
||
result: false,
|
||
reason: err.message
|
||
};
|
||
}
|
||
}
|
||
|
||
checkIfFileAlreadyExists(fileID) {
|
||
const {
|
||
files
|
||
} = this.getState();
|
||
|
||
if (files[fileID] && !files[fileID].isGhost) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
/**
|
||
* Create a file state object based on user-provided `addFile()` options.
|
||
*
|
||
* Note this is extremely side-effectful and should only be done when a file state object
|
||
* will be added to state immediately afterward!
|
||
*
|
||
* The `files` value is passed in because it may be updated by the caller without updating the store.
|
||
*/
|
||
|
||
|
||
/**
|
||
* Add a new file to `state.files`. This will run `onBeforeFileAdded`,
|
||
* try to guess file type in a clever way, check file against restrictions,
|
||
* and start an upload if `autoProceed === true`.
|
||
*
|
||
* @param {object} file object to add
|
||
* @returns {string} id for the added file
|
||
*/
|
||
addFile(file) {
|
||
_classPrivateFieldLooseBase(this, _assertNewUploadAllowed)[_assertNewUploadAllowed](file);
|
||
|
||
const {
|
||
files
|
||
} = this.getState();
|
||
|
||
let newFile = _classPrivateFieldLooseBase(this, _checkAndCreateFileStateObject)[_checkAndCreateFileStateObject](files, file); // Users are asked to re-select recovered files without data,
|
||
// and to keep the progress, meta and everthing else, we only replace said data
|
||
|
||
|
||
if (files[newFile.id] && files[newFile.id].isGhost) {
|
||
newFile = { ...files[newFile.id],
|
||
data: file.data,
|
||
isGhost: false
|
||
};
|
||
this.log(`Replaced the blob in the restored ghost file: ${newFile.name}, ${newFile.id}`);
|
||
}
|
||
|
||
this.setState({
|
||
files: { ...files,
|
||
[newFile.id]: newFile
|
||
}
|
||
});
|
||
this.emit('file-added', newFile);
|
||
this.emit('files-added', [newFile]);
|
||
this.log(`Added file: ${newFile.name}, ${newFile.id}, mime type: ${newFile.type}`);
|
||
|
||
_classPrivateFieldLooseBase(this, _startIfAutoProceed)[_startIfAutoProceed]();
|
||
|
||
return newFile.id;
|
||
}
|
||
/**
|
||
* Add multiple files to `state.files`. See the `addFile()` documentation.
|
||
*
|
||
* If an error occurs while adding a file, it is logged and the user is notified.
|
||
* This is good for UI plugins, but not for programmatic use.
|
||
* Programmatic users should usually still use `addFile()` on individual files.
|
||
*/
|
||
|
||
|
||
addFiles(fileDescriptors) {
|
||
_classPrivateFieldLooseBase(this, _assertNewUploadAllowed)[_assertNewUploadAllowed](); // create a copy of the files object only once
|
||
|
||
|
||
const files = { ...this.getState().files
|
||
};
|
||
const newFiles = [];
|
||
const errors = [];
|
||
|
||
for (let i = 0; i < fileDescriptors.length; i++) {
|
||
try {
|
||
let newFile = _classPrivateFieldLooseBase(this, _checkAndCreateFileStateObject)[_checkAndCreateFileStateObject](files, fileDescriptors[i]); // Users are asked to re-select recovered files without data,
|
||
// and to keep the progress, meta and everthing else, we only replace said data
|
||
|
||
|
||
if (files[newFile.id] && files[newFile.id].isGhost) {
|
||
newFile = { ...files[newFile.id],
|
||
data: fileDescriptors[i].data,
|
||
isGhost: false
|
||
};
|
||
this.log(`Replaced blob in a ghost file: ${newFile.name}, ${newFile.id}`);
|
||
}
|
||
|
||
files[newFile.id] = newFile;
|
||
newFiles.push(newFile);
|
||
} catch (err) {
|
||
if (!err.isRestriction) {
|
||
errors.push(err);
|
||
}
|
||
}
|
||
}
|
||
|
||
this.setState({
|
||
files
|
||
});
|
||
newFiles.forEach(newFile => {
|
||
this.emit('file-added', newFile);
|
||
});
|
||
this.emit('files-added', newFiles);
|
||
|
||
if (newFiles.length > 5) {
|
||
this.log(`Added batch of ${newFiles.length} files`);
|
||
} else {
|
||
Object.keys(newFiles).forEach(fileID => {
|
||
this.log(`Added file: ${newFiles[fileID].name}\n id: ${newFiles[fileID].id}\n type: ${newFiles[fileID].type}`);
|
||
});
|
||
}
|
||
|
||
if (newFiles.length > 0) {
|
||
_classPrivateFieldLooseBase(this, _startIfAutoProceed)[_startIfAutoProceed]();
|
||
}
|
||
|
||
if (errors.length > 0) {
|
||
let message = 'Multiple errors occurred while adding files:\n';
|
||
errors.forEach(subError => {
|
||
message += `\n * ${subError.message}`;
|
||
});
|
||
this.info({
|
||
message: this.i18n('addBulkFilesFailed', {
|
||
smart_count: errors.length
|
||
}),
|
||
details: message
|
||
}, 'error', this.opts.infoTimeout);
|
||
|
||
if (typeof AggregateError === 'function') {
|
||
throw new AggregateError(errors, message);
|
||
} else {
|
||
const err = new Error(message);
|
||
err.errors = errors;
|
||
throw err;
|
||
}
|
||
}
|
||
}
|
||
|
||
removeFiles(fileIDs, reason) {
|
||
const {
|
||
files,
|
||
currentUploads
|
||
} = this.getState();
|
||
const updatedFiles = { ...files
|
||
};
|
||
const updatedUploads = { ...currentUploads
|
||
};
|
||
const removedFiles = Object.create(null);
|
||
fileIDs.forEach(fileID => {
|
||
if (files[fileID]) {
|
||
removedFiles[fileID] = files[fileID];
|
||
delete updatedFiles[fileID];
|
||
}
|
||
}); // Remove files from the `fileIDs` list in each upload.
|
||
|
||
function fileIsNotRemoved(uploadFileID) {
|
||
return removedFiles[uploadFileID] === undefined;
|
||
}
|
||
|
||
Object.keys(updatedUploads).forEach(uploadID => {
|
||
const newFileIDs = currentUploads[uploadID].fileIDs.filter(fileIsNotRemoved); // Remove the upload if no files are associated with it anymore.
|
||
|
||
if (newFileIDs.length === 0) {
|
||
delete updatedUploads[uploadID];
|
||
return;
|
||
}
|
||
|
||
updatedUploads[uploadID] = { ...currentUploads[uploadID],
|
||
fileIDs: newFileIDs
|
||
};
|
||
});
|
||
const stateUpdate = {
|
||
currentUploads: updatedUploads,
|
||
files: updatedFiles
|
||
}; // If all files were removed - allow new uploads,
|
||
// and clear recoveredState
|
||
|
||
if (Object.keys(updatedFiles).length === 0) {
|
||
stateUpdate.allowNewUpload = true;
|
||
stateUpdate.error = null;
|
||
stateUpdate.recoveredState = null;
|
||
}
|
||
|
||
this.setState(stateUpdate);
|
||
this.calculateTotalProgress();
|
||
const removedFileIDs = Object.keys(removedFiles);
|
||
removedFileIDs.forEach(fileID => {
|
||
this.emit('file-removed', removedFiles[fileID], reason);
|
||
});
|
||
|
||
if (removedFileIDs.length > 5) {
|
||
this.log(`Removed ${removedFileIDs.length} files`);
|
||
} else {
|
||
this.log(`Removed files: ${removedFileIDs.join(', ')}`);
|
||
}
|
||
}
|
||
|
||
removeFile(fileID, reason) {
|
||
if (reason === void 0) {
|
||
reason = null;
|
||
}
|
||
|
||
this.removeFiles([fileID], reason);
|
||
}
|
||
|
||
pauseResume(fileID) {
|
||
if (!this.getState().capabilities.resumableUploads || this.getFile(fileID).uploadComplete) {
|
||
return undefined;
|
||
}
|
||
|
||
const wasPaused = this.getFile(fileID).isPaused || false;
|
||
const isPaused = !wasPaused;
|
||
this.setFileState(fileID, {
|
||
isPaused
|
||
});
|
||
this.emit('upload-pause', fileID, isPaused);
|
||
return isPaused;
|
||
}
|
||
|
||
pauseAll() {
|
||
const updatedFiles = { ...this.getState().files
|
||
};
|
||
const inProgressUpdatedFiles = Object.keys(updatedFiles).filter(file => {
|
||
return !updatedFiles[file].progress.uploadComplete && updatedFiles[file].progress.uploadStarted;
|
||
});
|
||
inProgressUpdatedFiles.forEach(file => {
|
||
const updatedFile = { ...updatedFiles[file],
|
||
isPaused: true
|
||
};
|
||
updatedFiles[file] = updatedFile;
|
||
});
|
||
this.setState({
|
||
files: updatedFiles
|
||
});
|
||
this.emit('pause-all');
|
||
}
|
||
|
||
resumeAll() {
|
||
const updatedFiles = { ...this.getState().files
|
||
};
|
||
const inProgressUpdatedFiles = Object.keys(updatedFiles).filter(file => {
|
||
return !updatedFiles[file].progress.uploadComplete && updatedFiles[file].progress.uploadStarted;
|
||
});
|
||
inProgressUpdatedFiles.forEach(file => {
|
||
const updatedFile = { ...updatedFiles[file],
|
||
isPaused: false,
|
||
error: null
|
||
};
|
||
updatedFiles[file] = updatedFile;
|
||
});
|
||
this.setState({
|
||
files: updatedFiles
|
||
});
|
||
this.emit('resume-all');
|
||
}
|
||
|
||
retryAll() {
|
||
const updatedFiles = { ...this.getState().files
|
||
};
|
||
const filesToRetry = Object.keys(updatedFiles).filter(file => {
|
||
return updatedFiles[file].error;
|
||
});
|
||
filesToRetry.forEach(file => {
|
||
const updatedFile = { ...updatedFiles[file],
|
||
isPaused: false,
|
||
error: null
|
||
};
|
||
updatedFiles[file] = updatedFile;
|
||
});
|
||
this.setState({
|
||
files: updatedFiles,
|
||
error: null
|
||
});
|
||
this.emit('retry-all', filesToRetry);
|
||
|
||
if (filesToRetry.length === 0) {
|
||
return Promise.resolve({
|
||
successful: [],
|
||
failed: []
|
||
});
|
||
}
|
||
|
||
const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](filesToRetry, {
|
||
forceAllowNewUpload: true // create new upload even if allowNewUpload: false
|
||
|
||
});
|
||
|
||
return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID);
|
||
}
|
||
|
||
cancelAll() {
|
||
this.emit('cancel-all');
|
||
const {
|
||
files
|
||
} = this.getState();
|
||
const fileIDs = Object.keys(files);
|
||
|
||
if (fileIDs.length) {
|
||
this.removeFiles(fileIDs, 'cancel-all');
|
||
}
|
||
|
||
this.setState({
|
||
totalProgress: 0,
|
||
error: null,
|
||
recoveredState: null
|
||
});
|
||
}
|
||
|
||
retryUpload(fileID) {
|
||
this.setFileState(fileID, {
|
||
error: null,
|
||
isPaused: false
|
||
});
|
||
this.emit('upload-retry', fileID);
|
||
|
||
const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload]([fileID], {
|
||
forceAllowNewUpload: true // create new upload even if allowNewUpload: false
|
||
|
||
});
|
||
|
||
return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID);
|
||
}
|
||
|
||
reset() {
|
||
this.cancelAll();
|
||
}
|
||
|
||
logout() {
|
||
this.iteratePlugins(plugin => {
|
||
if (plugin.provider && plugin.provider.logout) {
|
||
plugin.provider.logout();
|
||
}
|
||
});
|
||
}
|
||
|
||
calculateProgress(file, data) {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
} // bytesTotal may be null or zero; in that case we can't divide by it
|
||
|
||
|
||
const canHavePercentage = Number.isFinite(data.bytesTotal) && data.bytesTotal > 0;
|
||
this.setFileState(file.id, {
|
||
progress: { ...this.getFile(file.id).progress,
|
||
bytesUploaded: data.bytesUploaded,
|
||
bytesTotal: data.bytesTotal,
|
||
percentage: canHavePercentage ? Math.round(data.bytesUploaded / data.bytesTotal * 100) : 0
|
||
}
|
||
});
|
||
this.calculateTotalProgress();
|
||
}
|
||
|
||
calculateTotalProgress() {
|
||
// calculate total progress, using the number of files currently uploading,
|
||
// multiplied by 100 and the summ of individual progress of each file
|
||
const files = this.getFiles();
|
||
const inProgress = files.filter(file => {
|
||
return file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess;
|
||
});
|
||
|
||
if (inProgress.length === 0) {
|
||
this.emit('progress', 0);
|
||
this.setState({
|
||
totalProgress: 0
|
||
});
|
||
return;
|
||
}
|
||
|
||
const sizedFiles = inProgress.filter(file => file.progress.bytesTotal != null);
|
||
const unsizedFiles = inProgress.filter(file => file.progress.bytesTotal == null);
|
||
|
||
if (sizedFiles.length === 0) {
|
||
const progressMax = inProgress.length * 100;
|
||
const currentProgress = unsizedFiles.reduce((acc, file) => {
|
||
return acc + file.progress.percentage;
|
||
}, 0);
|
||
const totalProgress = Math.round(currentProgress / progressMax * 100);
|
||
this.setState({
|
||
totalProgress
|
||
});
|
||
return;
|
||
}
|
||
|
||
let totalSize = sizedFiles.reduce((acc, file) => {
|
||
return acc + file.progress.bytesTotal;
|
||
}, 0);
|
||
const averageSize = totalSize / sizedFiles.length;
|
||
totalSize += averageSize * unsizedFiles.length;
|
||
let uploadedSize = 0;
|
||
sizedFiles.forEach(file => {
|
||
uploadedSize += file.progress.bytesUploaded;
|
||
});
|
||
unsizedFiles.forEach(file => {
|
||
uploadedSize += averageSize * (file.progress.percentage || 0) / 100;
|
||
});
|
||
let totalProgress = totalSize === 0 ? 0 : Math.round(uploadedSize / totalSize * 100); // hot fix, because:
|
||
// uploadedSize ended up larger than totalSize, resulting in 1325% total
|
||
|
||
if (totalProgress > 100) {
|
||
totalProgress = 100;
|
||
}
|
||
|
||
this.setState({
|
||
totalProgress
|
||
});
|
||
this.emit('progress', totalProgress);
|
||
}
|
||
/**
|
||
* Registers listeners for all global actions, like:
|
||
* `error`, `file-removed`, `upload-progress`
|
||
*/
|
||
|
||
|
||
updateOnlineStatus() {
|
||
const online = typeof window.navigator.onLine !== 'undefined' ? window.navigator.onLine : true;
|
||
|
||
if (!online) {
|
||
this.emit('is-offline');
|
||
this.info(this.i18n('noInternetConnection'), 'error', 0);
|
||
this.wasOffline = true;
|
||
} else {
|
||
this.emit('is-online');
|
||
|
||
if (this.wasOffline) {
|
||
this.emit('back-online');
|
||
this.info(this.i18n('connectedToInternet'), 'success', 3000);
|
||
this.wasOffline = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
getID() {
|
||
return this.opts.id;
|
||
}
|
||
/**
|
||
* Registers a plugin with Core.
|
||
*
|
||
* @param {object} Plugin object
|
||
* @param {object} [opts] object with options to be passed to Plugin
|
||
* @returns {object} self for chaining
|
||
*/
|
||
// eslint-disable-next-line no-shadow
|
||
|
||
|
||
use(Plugin, opts) {
|
||
if (typeof Plugin !== 'function') {
|
||
const msg = `Expected a plugin class, but got ${Plugin === null ? 'null' : typeof Plugin}.` + ' Please verify that the plugin was imported and spelled correctly.';
|
||
throw new TypeError(msg);
|
||
} // Instantiate
|
||
|
||
|
||
const plugin = new Plugin(this, opts);
|
||
const pluginId = plugin.id;
|
||
|
||
if (!pluginId) {
|
||
throw new Error('Your plugin must have an id');
|
||
}
|
||
|
||
if (!plugin.type) {
|
||
throw new Error('Your plugin must have a type');
|
||
}
|
||
|
||
const existsPluginAlready = this.getPlugin(pluginId);
|
||
|
||
if (existsPluginAlready) {
|
||
const msg = `Already found a plugin named '${existsPluginAlready.id}'. ` + `Tried to use: '${pluginId}'.\n` + 'Uppy plugins must have unique `id` options. See https://uppy.io/docs/plugins/#id.';
|
||
throw new Error(msg);
|
||
}
|
||
|
||
if (Plugin.VERSION) {
|
||
this.log(`Using ${pluginId} v${Plugin.VERSION}`);
|
||
}
|
||
|
||
if (plugin.type in _classPrivateFieldLooseBase(this, _plugins)[_plugins]) {
|
||
_classPrivateFieldLooseBase(this, _plugins)[_plugins][plugin.type].push(plugin);
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _plugins)[_plugins][plugin.type] = [plugin];
|
||
}
|
||
|
||
plugin.install();
|
||
return this;
|
||
}
|
||
/**
|
||
* Find one Plugin by name.
|
||
*
|
||
* @param {string} id plugin id
|
||
* @returns {BasePlugin|undefined}
|
||
*/
|
||
|
||
|
||
getPlugin(id) {
|
||
for (const plugins of Object.values(_classPrivateFieldLooseBase(this, _plugins)[_plugins])) {
|
||
const foundPlugin = plugins.find(plugin => plugin.id === id);
|
||
if (foundPlugin != null) return foundPlugin;
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
[_Symbol$for](type) {
|
||
return _classPrivateFieldLooseBase(this, _plugins)[_plugins][type];
|
||
}
|
||
/**
|
||
* Iterate through all `use`d plugins.
|
||
*
|
||
* @param {Function} method that will be run on each plugin
|
||
*/
|
||
|
||
|
||
iteratePlugins(method) {
|
||
Object.values(_classPrivateFieldLooseBase(this, _plugins)[_plugins]).flat(1).forEach(method);
|
||
}
|
||
/**
|
||
* Uninstall and remove a plugin.
|
||
*
|
||
* @param {object} instance The plugin instance to remove.
|
||
*/
|
||
|
||
|
||
removePlugin(instance) {
|
||
this.log(`Removing plugin ${instance.id}`);
|
||
this.emit('plugin-remove', instance);
|
||
|
||
if (instance.uninstall) {
|
||
instance.uninstall();
|
||
}
|
||
|
||
const list = _classPrivateFieldLooseBase(this, _plugins)[_plugins][instance.type]; // list.indexOf failed here, because Vue3 converted the plugin instance
|
||
// to a Proxy object, which failed the strict comparison test:
|
||
// obj !== objProxy
|
||
|
||
|
||
const index = list.findIndex(item => item.id === instance.id);
|
||
|
||
if (index !== -1) {
|
||
list.splice(index, 1);
|
||
}
|
||
|
||
const state = this.getState();
|
||
const updatedState = {
|
||
plugins: { ...state.plugins,
|
||
[instance.id]: undefined
|
||
}
|
||
};
|
||
this.setState(updatedState);
|
||
}
|
||
/**
|
||
* Uninstall all plugins and close down this Uppy instance.
|
||
*/
|
||
|
||
|
||
close() {
|
||
this.log(`Closing Uppy instance ${this.opts.id}: removing all files and uninstalling plugins`);
|
||
this.reset();
|
||
|
||
_classPrivateFieldLooseBase(this, _storeUnsubscribe)[_storeUnsubscribe]();
|
||
|
||
this.iteratePlugins(plugin => {
|
||
this.removePlugin(plugin);
|
||
});
|
||
|
||
if (typeof window !== 'undefined' && window.removeEventListener) {
|
||
window.removeEventListener('online', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]);
|
||
window.removeEventListener('offline', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]);
|
||
}
|
||
}
|
||
|
||
hideInfo() {
|
||
const {
|
||
info
|
||
} = this.getState();
|
||
this.setState({
|
||
info: info.slice(1)
|
||
});
|
||
this.emit('info-hidden');
|
||
}
|
||
/**
|
||
* Set info message in `state.info`, so that UI plugins like `Informer`
|
||
* can display the message.
|
||
*
|
||
* @param {string | object} message Message to be displayed by the informer
|
||
* @param {string} [type]
|
||
* @param {number} [duration]
|
||
*/
|
||
|
||
|
||
info(message, type, duration) {
|
||
if (type === void 0) {
|
||
type = 'info';
|
||
}
|
||
|
||
if (duration === void 0) {
|
||
duration = 3000;
|
||
}
|
||
|
||
const isComplexMessage = typeof message === 'object';
|
||
this.setState({
|
||
info: [...this.getState().info, {
|
||
type,
|
||
message: isComplexMessage ? message.message : message,
|
||
details: isComplexMessage ? message.details : null
|
||
}]
|
||
});
|
||
setTimeout(() => this.hideInfo(), duration);
|
||
this.emit('info-visible');
|
||
}
|
||
/**
|
||
* Passes messages to a function, provided in `opts.logger`.
|
||
* If `opts.logger: Uppy.debugLogger` or `opts.debug: true`, logs to the browser console.
|
||
*
|
||
* @param {string|object} message to log
|
||
* @param {string} [type] optional `error` or `warning`
|
||
*/
|
||
|
||
|
||
log(message, type) {
|
||
const {
|
||
logger
|
||
} = this.opts;
|
||
|
||
switch (type) {
|
||
case 'error':
|
||
logger.error(message);
|
||
break;
|
||
|
||
case 'warning':
|
||
logger.warn(message);
|
||
break;
|
||
|
||
default:
|
||
logger.debug(message);
|
||
break;
|
||
}
|
||
}
|
||
/**
|
||
* Restore an upload by its ID.
|
||
*/
|
||
|
||
|
||
restore(uploadID) {
|
||
this.log(`Core: attempting to restore upload "${uploadID}"`);
|
||
|
||
if (!this.getState().currentUploads[uploadID]) {
|
||
_classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID);
|
||
|
||
return Promise.reject(new Error('Nonexistent upload'));
|
||
}
|
||
|
||
return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID);
|
||
}
|
||
/**
|
||
* Create an upload for a bunch of files.
|
||
*
|
||
* @param {Array<string>} fileIDs File IDs to include in this upload.
|
||
* @returns {string} ID of this upload.
|
||
*/
|
||
|
||
|
||
[_Symbol$for2]() {
|
||
return _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](...arguments);
|
||
}
|
||
|
||
/**
|
||
* Add data to an upload's result object.
|
||
*
|
||
* @param {string} uploadID The ID of the upload.
|
||
* @param {object} data Data properties to add to the result object.
|
||
*/
|
||
addResultData(uploadID, data) {
|
||
if (!_classPrivateFieldLooseBase(this, _getUpload)[_getUpload](uploadID)) {
|
||
this.log(`Not setting result for an upload that has been removed: ${uploadID}`);
|
||
return;
|
||
}
|
||
|
||
const {
|
||
currentUploads
|
||
} = this.getState();
|
||
const currentUpload = { ...currentUploads[uploadID],
|
||
result: { ...currentUploads[uploadID].result,
|
||
...data
|
||
}
|
||
};
|
||
this.setState({
|
||
currentUploads: { ...currentUploads,
|
||
[uploadID]: currentUpload
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* Remove an upload, eg. if it has been canceled or completed.
|
||
*
|
||
* @param {string} uploadID The ID of the upload.
|
||
*/
|
||
|
||
|
||
/**
|
||
* Start an upload for all the files that are not currently being uploaded.
|
||
*
|
||
* @returns {Promise}
|
||
*/
|
||
upload() {
|
||
var _classPrivateFieldLoo;
|
||
|
||
if (!((_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _plugins)[_plugins].uploader) != null && _classPrivateFieldLoo.length)) {
|
||
this.log('No uploader type plugins are used', 'warning');
|
||
}
|
||
|
||
let {
|
||
files
|
||
} = this.getState();
|
||
const onBeforeUploadResult = this.opts.onBeforeUpload(files);
|
||
|
||
if (onBeforeUploadResult === false) {
|
||
return Promise.reject(new Error('Not starting the upload because onBeforeUpload returned false'));
|
||
}
|
||
|
||
if (onBeforeUploadResult && typeof onBeforeUploadResult === 'object') {
|
||
files = onBeforeUploadResult; // Updating files in state, because uploader plugins receive file IDs,
|
||
// and then fetch the actual file object from state
|
||
|
||
this.setState({
|
||
files
|
||
});
|
||
}
|
||
|
||
return Promise.resolve().then(() => _classPrivateFieldLooseBase(this, _restricter)[_restricter].validateMinNumberOfFiles(files)).catch(err => {
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](err);
|
||
|
||
throw err;
|
||
}).then(() => {
|
||
if (!_classPrivateFieldLooseBase(this, _checkRequiredMetaFields)[_checkRequiredMetaFields](files)) {
|
||
throw new RestrictionError(this.i18n('missingRequiredMetaField'));
|
||
}
|
||
}).catch(err => {
|
||
// Doing this in a separate catch because we already emited and logged
|
||
// all the errors in `checkRequiredMetaFields` so we only throw a generic
|
||
// missing fields error here.
|
||
throw err;
|
||
}).then(() => {
|
||
const {
|
||
currentUploads
|
||
} = this.getState(); // get a list of files that are currently assigned to uploads
|
||
|
||
const currentlyUploadingFiles = Object.values(currentUploads).flatMap(curr => curr.fileIDs);
|
||
const waitingFileIDs = [];
|
||
Object.keys(files).forEach(fileID => {
|
||
const file = this.getFile(fileID); // if the file hasn't started uploading and hasn't already been assigned to an upload..
|
||
|
||
if (!file.progress.uploadStarted && currentlyUploadingFiles.indexOf(fileID) === -1) {
|
||
waitingFileIDs.push(file.id);
|
||
}
|
||
});
|
||
|
||
const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](waitingFileIDs);
|
||
|
||
return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID);
|
||
}).catch(err => {
|
||
this.emit('error', err);
|
||
this.log(err, 'error');
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
function _informAndEmit2(error, file) {
|
||
const {
|
||
message,
|
||
details = ''
|
||
} = error;
|
||
|
||
if (error.isRestriction) {
|
||
this.emit('restriction-failed', file, error);
|
||
} else {
|
||
this.emit('error', error);
|
||
}
|
||
|
||
this.info({
|
||
message,
|
||
details
|
||
}, 'error', this.opts.infoTimeout);
|
||
this.log(`${message} ${details}`.trim(), 'error');
|
||
}
|
||
|
||
function _checkRequiredMetaFieldsOnFile2(file) {
|
||
const {
|
||
missingFields,
|
||
error
|
||
} = _classPrivateFieldLooseBase(this, _restricter)[_restricter].getMissingRequiredMetaFields(file);
|
||
|
||
if (missingFields.length > 0) {
|
||
this.setFileState(file.id, {
|
||
missingRequiredMetaFields: missingFields
|
||
});
|
||
this.log(error.message);
|
||
this.emit('restriction-failed', file, error);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function _checkRequiredMetaFields2(files) {
|
||
let success = true;
|
||
|
||
for (const file of Object.values(files)) {
|
||
if (!_classPrivateFieldLooseBase(this, _checkRequiredMetaFieldsOnFile)[_checkRequiredMetaFieldsOnFile](file)) {
|
||
success = false;
|
||
}
|
||
}
|
||
|
||
return success;
|
||
}
|
||
|
||
function _assertNewUploadAllowed2(file) {
|
||
const {
|
||
allowNewUpload
|
||
} = this.getState();
|
||
|
||
if (allowNewUpload === false) {
|
||
const error = new RestrictionError(this.i18n('noMoreFilesAllowed'));
|
||
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](error, file);
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
function _checkAndCreateFileStateObject2(files, fileDescriptor) {
|
||
const fileType = getFileType(fileDescriptor);
|
||
const fileName = getFileName(fileType, fileDescriptor);
|
||
const fileExtension = getFileNameAndExtension(fileName).extension;
|
||
const isRemote = Boolean(fileDescriptor.isRemote);
|
||
const fileID = generateFileID({ ...fileDescriptor,
|
||
type: fileType
|
||
});
|
||
|
||
if (this.checkIfFileAlreadyExists(fileID)) {
|
||
const error = new RestrictionError(this.i18n('noDuplicates', {
|
||
fileName
|
||
}));
|
||
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](error, fileDescriptor);
|
||
|
||
throw error;
|
||
}
|
||
|
||
const meta = fileDescriptor.meta || {};
|
||
meta.name = fileName;
|
||
meta.type = fileType; // `null` means the size is unknown.
|
||
|
||
const size = Number.isFinite(fileDescriptor.data.size) ? fileDescriptor.data.size : null;
|
||
let newFile = {
|
||
source: fileDescriptor.source || '',
|
||
id: fileID,
|
||
name: fileName,
|
||
extension: fileExtension || '',
|
||
meta: { ...this.getState().meta,
|
||
...meta
|
||
},
|
||
type: fileType,
|
||
data: fileDescriptor.data,
|
||
progress: {
|
||
percentage: 0,
|
||
bytesUploaded: 0,
|
||
bytesTotal: size,
|
||
uploadComplete: false,
|
||
uploadStarted: null
|
||
},
|
||
size,
|
||
isRemote,
|
||
remote: fileDescriptor.remote || '',
|
||
preview: fileDescriptor.preview
|
||
};
|
||
const onBeforeFileAddedResult = this.opts.onBeforeFileAdded(newFile, files);
|
||
|
||
if (onBeforeFileAddedResult === false) {
|
||
// Don’t show UI info for this error, as it should be done by the developer
|
||
const error = new RestrictionError('Cannot add the file because onBeforeFileAdded returned false.');
|
||
this.emit('restriction-failed', fileDescriptor, error);
|
||
throw error;
|
||
} else if (typeof onBeforeFileAddedResult === 'object' && onBeforeFileAddedResult !== null) {
|
||
newFile = onBeforeFileAddedResult;
|
||
}
|
||
|
||
try {
|
||
const filesArray = Object.keys(files).map(i => files[i]);
|
||
|
||
_classPrivateFieldLooseBase(this, _restricter)[_restricter].validate(newFile, filesArray);
|
||
} catch (err) {
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](err, newFile);
|
||
|
||
throw err;
|
||
}
|
||
|
||
return newFile;
|
||
}
|
||
|
||
function _startIfAutoProceed2() {
|
||
if (this.opts.autoProceed && !this.scheduledAutoProceed) {
|
||
this.scheduledAutoProceed = setTimeout(() => {
|
||
this.scheduledAutoProceed = null;
|
||
this.upload().catch(err => {
|
||
if (!err.isRestriction) {
|
||
this.log(err.stack || err.message || err);
|
||
}
|
||
});
|
||
}, 4);
|
||
}
|
||
}
|
||
|
||
function _addListeners2() {
|
||
/**
|
||
* @param {Error} error
|
||
* @param {object} [file]
|
||
* @param {object} [response]
|
||
*/
|
||
const errorHandler = (error, file, response) => {
|
||
let errorMsg = error.message || 'Unknown error';
|
||
|
||
if (error.details) {
|
||
errorMsg += ` ${error.details}`;
|
||
}
|
||
|
||
this.setState({
|
||
error: errorMsg
|
||
});
|
||
|
||
if (file != null && file.id in this.getState().files) {
|
||
this.setFileState(file.id, {
|
||
error: errorMsg,
|
||
response
|
||
});
|
||
}
|
||
};
|
||
|
||
this.on('error', errorHandler);
|
||
this.on('upload-error', (file, error, response) => {
|
||
errorHandler(error, file, response);
|
||
|
||
if (typeof error === 'object' && error.message) {
|
||
const newError = new Error(error.message);
|
||
newError.details = error.message;
|
||
|
||
if (error.details) {
|
||
newError.details += ` ${error.details}`;
|
||
}
|
||
|
||
newError.message = this.i18n('failedToUpload', {
|
||
file: file.name
|
||
});
|
||
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](newError);
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _informAndEmit)[_informAndEmit](error);
|
||
}
|
||
});
|
||
this.on('upload', () => {
|
||
this.setState({
|
||
error: null
|
||
});
|
||
});
|
||
this.on('upload-started', file => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
this.setFileState(file.id, {
|
||
progress: {
|
||
uploadStarted: Date.now(),
|
||
uploadComplete: false,
|
||
percentage: 0,
|
||
bytesUploaded: 0,
|
||
bytesTotal: file.size
|
||
}
|
||
});
|
||
});
|
||
this.on('upload-progress', this.calculateProgress);
|
||
this.on('upload-success', (file, uploadResp) => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
const currentProgress = this.getFile(file.id).progress;
|
||
this.setFileState(file.id, {
|
||
progress: { ...currentProgress,
|
||
postprocess: _classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].size > 0 ? {
|
||
mode: 'indeterminate'
|
||
} : null,
|
||
uploadComplete: true,
|
||
percentage: 100,
|
||
bytesUploaded: currentProgress.bytesTotal
|
||
},
|
||
response: uploadResp,
|
||
uploadURL: uploadResp.uploadURL,
|
||
isPaused: false
|
||
}); // Remote providers sometimes don't tell us the file size,
|
||
// but we can know how many bytes we uploaded once the upload is complete.
|
||
|
||
if (file.size == null) {
|
||
this.setFileState(file.id, {
|
||
size: uploadResp.bytesUploaded || currentProgress.bytesTotal
|
||
});
|
||
}
|
||
|
||
this.calculateTotalProgress();
|
||
});
|
||
this.on('preprocess-progress', (file, progress) => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
this.setFileState(file.id, {
|
||
progress: { ...this.getFile(file.id).progress,
|
||
preprocess: progress
|
||
}
|
||
});
|
||
});
|
||
this.on('preprocess-complete', file => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
const files = { ...this.getState().files
|
||
};
|
||
files[file.id] = { ...files[file.id],
|
||
progress: { ...files[file.id].progress
|
||
}
|
||
};
|
||
delete files[file.id].progress.preprocess;
|
||
this.setState({
|
||
files
|
||
});
|
||
});
|
||
this.on('postprocess-progress', (file, progress) => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
this.setFileState(file.id, {
|
||
progress: { ...this.getState().files[file.id].progress,
|
||
postprocess: progress
|
||
}
|
||
});
|
||
});
|
||
this.on('postprocess-complete', file => {
|
||
if (!this.getFile(file.id)) {
|
||
this.log(`Not setting progress for a file that has been removed: ${file.id}`);
|
||
return;
|
||
}
|
||
|
||
const files = { ...this.getState().files
|
||
};
|
||
files[file.id] = { ...files[file.id],
|
||
progress: { ...files[file.id].progress
|
||
}
|
||
};
|
||
delete files[file.id].progress.postprocess;
|
||
this.setState({
|
||
files
|
||
});
|
||
});
|
||
this.on('restored', () => {
|
||
// Files may have changed--ensure progress is still accurate.
|
||
this.calculateTotalProgress();
|
||
});
|
||
this.on('dashboard:file-edit-complete', file => {
|
||
if (file) {
|
||
_classPrivateFieldLooseBase(this, _checkRequiredMetaFieldsOnFile)[_checkRequiredMetaFieldsOnFile](file);
|
||
}
|
||
}); // show informer if offline
|
||
|
||
if (typeof window !== 'undefined' && window.addEventListener) {
|
||
window.addEventListener('online', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]);
|
||
window.addEventListener('offline', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]);
|
||
setTimeout(_classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus], 3000);
|
||
}
|
||
}
|
||
|
||
function _createUpload2(fileIDs, opts) {
|
||
if (opts === void 0) {
|
||
opts = {};
|
||
}
|
||
|
||
// uppy.retryAll sets this to true — when retrying we want to ignore `allowNewUpload: false`
|
||
const {
|
||
forceAllowNewUpload = false
|
||
} = opts;
|
||
const {
|
||
allowNewUpload,
|
||
currentUploads
|
||
} = this.getState();
|
||
|
||
if (!allowNewUpload && !forceAllowNewUpload) {
|
||
throw new Error('Cannot create a new upload: already uploading.');
|
||
}
|
||
|
||
const uploadID = nanoid();
|
||
this.emit('upload', {
|
||
id: uploadID,
|
||
fileIDs
|
||
});
|
||
this.setState({
|
||
allowNewUpload: this.opts.allowMultipleUploadBatches !== false && this.opts.allowMultipleUploads !== false,
|
||
currentUploads: { ...currentUploads,
|
||
[uploadID]: {
|
||
fileIDs,
|
||
step: 0,
|
||
result: {}
|
||
}
|
||
}
|
||
});
|
||
return uploadID;
|
||
}
|
||
|
||
function _getUpload2(uploadID) {
|
||
const {
|
||
currentUploads
|
||
} = this.getState();
|
||
return currentUploads[uploadID];
|
||
}
|
||
|
||
function _removeUpload2(uploadID) {
|
||
const currentUploads = { ...this.getState().currentUploads
|
||
};
|
||
delete currentUploads[uploadID];
|
||
this.setState({
|
||
currentUploads
|
||
});
|
||
}
|
||
|
||
async function _runUpload2(uploadID) {
|
||
let {
|
||
currentUploads
|
||
} = this.getState();
|
||
let currentUpload = currentUploads[uploadID];
|
||
const restoreStep = currentUpload.step || 0;
|
||
const steps = [..._classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors], ..._classPrivateFieldLooseBase(this, _uploaders)[_uploaders], ..._classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors]];
|
||
|
||
try {
|
||
for (let step = restoreStep; step < steps.length; step++) {
|
||
if (!currentUpload) {
|
||
break;
|
||
}
|
||
|
||
const fn = steps[step];
|
||
const updatedUpload = { ...currentUpload,
|
||
step
|
||
};
|
||
this.setState({
|
||
currentUploads: { ...currentUploads,
|
||
[uploadID]: updatedUpload
|
||
}
|
||
}); // TODO give this the `updatedUpload` object as its only parameter maybe?
|
||
// Otherwise when more metadata may be added to the upload this would keep getting more parameters
|
||
|
||
await fn(updatedUpload.fileIDs, uploadID); // Update currentUpload value in case it was modified asynchronously.
|
||
|
||
currentUploads = this.getState().currentUploads;
|
||
currentUpload = currentUploads[uploadID];
|
||
}
|
||
} catch (err) {
|
||
_classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID);
|
||
|
||
throw err;
|
||
} // Set result data.
|
||
|
||
|
||
if (currentUpload) {
|
||
// Mark postprocessing step as complete if necessary; this addresses a case where we might get
|
||
// stuck in the postprocessing UI while the upload is fully complete.
|
||
// If the postprocessing steps do not do any work, they may not emit postprocessing events at
|
||
// all, and never mark the postprocessing as complete. This is fine on its own but we
|
||
// introduced code in the @uppy/core upload-success handler to prepare postprocessing progress
|
||
// state if any postprocessors are registered. That is to avoid a "flash of completed state"
|
||
// before the postprocessing plugins can emit events.
|
||
//
|
||
// So, just in case an upload with postprocessing plugins *has* completed *without* emitting
|
||
// postprocessing completion, we do it instead.
|
||
currentUpload.fileIDs.forEach(fileID => {
|
||
const file = this.getFile(fileID);
|
||
|
||
if (file && file.progress.postprocess) {
|
||
this.emit('postprocess-complete', file);
|
||
}
|
||
});
|
||
const files = currentUpload.fileIDs.map(fileID => this.getFile(fileID));
|
||
const successful = files.filter(file => !file.error);
|
||
const failed = files.filter(file => file.error);
|
||
await this.addResultData(uploadID, {
|
||
successful,
|
||
failed,
|
||
uploadID
|
||
}); // Update currentUpload value in case it was modified asynchronously.
|
||
|
||
currentUploads = this.getState().currentUploads;
|
||
currentUpload = currentUploads[uploadID];
|
||
} // Emit completion events.
|
||
// This is in a separate function so that the `currentUploads` variable
|
||
// always refers to the latest state. In the handler right above it refers
|
||
// to an outdated object without the `.result` property.
|
||
|
||
|
||
let result;
|
||
|
||
if (currentUpload) {
|
||
result = currentUpload.result;
|
||
this.emit('complete', result);
|
||
|
||
_classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID);
|
||
}
|
||
|
||
if (result == null) {
|
||
this.log(`Not setting result for an upload that has been removed: ${uploadID}`);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
Uppy.VERSION = "2.1.6";
|
||
module.exports = Uppy;
|
||
},{"./Restricter":16,"./getFileName":19,"./locale":21,"./loggers":22,"./supportsUploadProgress":23,"@uppy/store-default":25,"@uppy/utils/lib/Translator":31,"@uppy/utils/lib/generateFileID":36,"@uppy/utils/lib/getFileNameAndExtension":42,"@uppy/utils/lib/getFileType":43,"lodash.throttle":54,"namespace-emitter":56,"nanoid/non-secure":57}],19:[function(require,module,exports){
|
||
module.exports = function getFileName(fileType, fileDescriptor) {
|
||
if (fileDescriptor.name) {
|
||
return fileDescriptor.name;
|
||
}
|
||
|
||
if (fileType.split('/')[0] === 'image') {
|
||
return `${fileType.split('/')[0]}.${fileType.split('/')[1]}`;
|
||
}
|
||
|
||
return 'noname';
|
||
};
|
||
},{}],20:[function(require,module,exports){
|
||
'use strict';
|
||
|
||
const Uppy = require('./Uppy');
|
||
|
||
const UIPlugin = require('./UIPlugin');
|
||
|
||
const BasePlugin = require('./BasePlugin');
|
||
|
||
const {
|
||
debugLogger
|
||
} = require('./loggers');
|
||
|
||
module.exports = Uppy;
|
||
module.exports.Uppy = Uppy;
|
||
module.exports.UIPlugin = UIPlugin;
|
||
module.exports.BasePlugin = BasePlugin;
|
||
module.exports.debugLogger = debugLogger;
|
||
},{"./BasePlugin":15,"./UIPlugin":17,"./Uppy":18,"./loggers":22}],21:[function(require,module,exports){
|
||
module.exports = {
|
||
strings: {
|
||
addBulkFilesFailed: {
|
||
0: 'Failed to add %{smart_count} file due to an internal error',
|
||
1: 'Failed to add %{smart_count} files due to internal errors'
|
||
},
|
||
youCanOnlyUploadX: {
|
||
0: 'You can only upload %{smart_count} file',
|
||
1: 'You can only upload %{smart_count} files'
|
||
},
|
||
youHaveToAtLeastSelectX: {
|
||
0: 'You have to select at least %{smart_count} file',
|
||
1: 'You have to select at least %{smart_count} files'
|
||
},
|
||
exceedsSize: '%{file} exceeds maximum allowed size of %{size}',
|
||
missingRequiredMetaField: 'Missing required meta fields',
|
||
missingRequiredMetaFieldOnFile: 'Missing required meta fields in %{fileName}',
|
||
inferiorSize: 'This file is smaller than the allowed size of %{size}',
|
||
youCanOnlyUploadFileTypes: 'You can only upload: %{types}',
|
||
noMoreFilesAllowed: 'Cannot add more files',
|
||
noDuplicates: "Cannot add the duplicate file '%{fileName}', it already exists",
|
||
companionError: 'Connection with Companion failed',
|
||
authAborted: 'Authentication aborted',
|
||
companionUnauthorizeHint: 'To unauthorize to your %{provider} account, please go to %{url}',
|
||
failedToUpload: 'Failed to upload %{file}',
|
||
noInternetConnection: 'No Internet connection',
|
||
connectedToInternet: 'Connected to the Internet',
|
||
// Strings for remote providers
|
||
noFilesFound: 'You have no files or folders here',
|
||
selectX: {
|
||
0: 'Select %{smart_count}',
|
||
1: 'Select %{smart_count}'
|
||
},
|
||
allFilesFromFolderNamed: 'All files from folder %{name}',
|
||
openFolderNamed: 'Open folder %{name}',
|
||
cancel: 'Cancel',
|
||
logOut: 'Log out',
|
||
filter: 'Filter',
|
||
resetFilter: 'Reset filter',
|
||
loading: 'Loading...',
|
||
authenticateWithTitle: 'Please authenticate with %{pluginName} to select files',
|
||
authenticateWith: 'Connect to %{pluginName}',
|
||
signInWithGoogle: 'Sign in with Google',
|
||
searchImages: 'Search for images',
|
||
enterTextToSearch: 'Enter text to search for images',
|
||
search: 'Search',
|
||
emptyFolderAdded: 'No files were added from empty folder',
|
||
folderAlreadyAdded: 'The folder "%{folder}" was already added',
|
||
folderAdded: {
|
||
0: 'Added %{smart_count} file from %{folder}',
|
||
1: 'Added %{smart_count} files from %{folder}'
|
||
}
|
||
}
|
||
};
|
||
},{}],22:[function(require,module,exports){
|
||
/* eslint-disable no-console */
|
||
const getTimeStamp = require('@uppy/utils/lib/getTimeStamp'); // Swallow all logs, except errors.
|
||
// default if logger is not set or debug: false
|
||
|
||
|
||
const justErrorsLogger = {
|
||
debug: () => {},
|
||
warn: () => {},
|
||
error: function () {
|
||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
args[_key] = arguments[_key];
|
||
}
|
||
|
||
return console.error(`[Uppy] [${getTimeStamp()}]`, ...args);
|
||
}
|
||
}; // Print logs to console with namespace + timestamp,
|
||
// set by logger: Uppy.debugLogger or debug: true
|
||
|
||
const debugLogger = {
|
||
debug: function () {
|
||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||
args[_key2] = arguments[_key2];
|
||
}
|
||
|
||
return console.debug(`[Uppy] [${getTimeStamp()}]`, ...args);
|
||
},
|
||
warn: function () {
|
||
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||
args[_key3] = arguments[_key3];
|
||
}
|
||
|
||
return console.warn(`[Uppy] [${getTimeStamp()}]`, ...args);
|
||
},
|
||
error: function () {
|
||
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||
args[_key4] = arguments[_key4];
|
||
}
|
||
|
||
return console.error(`[Uppy] [${getTimeStamp()}]`, ...args);
|
||
}
|
||
};
|
||
module.exports = {
|
||
justErrorsLogger,
|
||
debugLogger
|
||
};
|
||
},{"@uppy/utils/lib/getTimeStamp":45}],23:[function(require,module,exports){
|
||
// Edge 15.x does not fire 'progress' events on uploads.
|
||
// See https://github.com/transloadit/uppy/issues/945
|
||
// And https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12224510/
|
||
module.exports = function supportsUploadProgress(userAgent) {
|
||
// Allow passing in userAgent for tests
|
||
if (userAgent == null) {
|
||
userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : null;
|
||
} // Assume it works because basically everything supports progress events.
|
||
|
||
|
||
if (!userAgent) return true;
|
||
const m = /Edge\/(\d+\.\d+)/.exec(userAgent);
|
||
if (!m) return true;
|
||
const edgeVersion = m[1];
|
||
let [major, minor] = edgeVersion.split('.');
|
||
major = parseInt(major, 10);
|
||
minor = parseInt(minor, 10); // Worked before:
|
||
// Edge 40.15063.0.0
|
||
// Microsoft EdgeHTML 15.15063
|
||
|
||
if (major < 15 || major === 15 && minor < 15063) {
|
||
return true;
|
||
} // Fixed in:
|
||
// Microsoft EdgeHTML 18.18218
|
||
|
||
|
||
if (major > 18 || major === 18 && minor >= 18218) {
|
||
return true;
|
||
} // other versions don't work.
|
||
|
||
|
||
return false;
|
||
};
|
||
},{}],24:[function(require,module,exports){
|
||
var _class, _temp;
|
||
|
||
const BasePlugin = require('@uppy/core/lib/BasePlugin');
|
||
|
||
const getDroppedFiles = require('@uppy/utils/lib/getDroppedFiles');
|
||
|
||
const toArray = require('@uppy/utils/lib/toArray');
|
||
/**
|
||
* Drop Target plugin
|
||
*
|
||
*/
|
||
|
||
|
||
module.exports = (_temp = _class = class DropTarget extends BasePlugin {
|
||
constructor(uppy, opts) {
|
||
super(uppy, opts);
|
||
|
||
this.addFiles = files => {
|
||
const descriptors = files.map(file => ({
|
||
source: this.id,
|
||
name: file.name,
|
||
type: file.type,
|
||
data: file,
|
||
meta: {
|
||
// path of the file relative to the ancestor directory the user selected.
|
||
// e.g. 'docs/Old Prague/airbnb.pdf'
|
||
relativePath: file.relativePath || null
|
||
}
|
||
}));
|
||
|
||
try {
|
||
this.uppy.addFiles(descriptors);
|
||
} catch (err) {
|
||
this.uppy.log(err);
|
||
}
|
||
};
|
||
|
||
this.isFileTransfer = event => {
|
||
var _event$dataTransfer$t;
|
||
|
||
const transferTypes = (_event$dataTransfer$t = event.dataTransfer.types) != null ? _event$dataTransfer$t : [];
|
||
return transferTypes.some(type => type === 'Files');
|
||
};
|
||
|
||
this.handleDrop = async event => {
|
||
var _this$opts$onDrop, _this$opts;
|
||
|
||
if (!this.isFileTransfer(event)) {
|
||
return;
|
||
}
|
||
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
clearTimeout(this.removeDragOverClassTimeout); // Remove dragover class
|
||
|
||
event.currentTarget.classList.remove('uppy-is-drag-over');
|
||
this.setPluginState({
|
||
isDraggingOver: false
|
||
}); // Let any acquirer plugin (Url/Webcam/etc.) handle drops to the root
|
||
|
||
this.uppy.iteratePlugins(plugin => {
|
||
if (plugin.type === 'acquirer') {
|
||
// Every Plugin with .type acquirer can define handleRootDrop(event)
|
||
plugin.handleRootDrop == null ? void 0 : plugin.handleRootDrop(event);
|
||
}
|
||
}); // Add all dropped files, handle errors
|
||
|
||
let executedDropErrorOnce = false;
|
||
|
||
const logDropError = error => {
|
||
this.uppy.log(error, 'error'); // In practice all drop errors are most likely the same,
|
||
// so let's just show one to avoid overwhelming the user
|
||
|
||
if (!executedDropErrorOnce) {
|
||
this.uppy.info(error.message, 'error');
|
||
executedDropErrorOnce = true;
|
||
}
|
||
};
|
||
|
||
const files = await getDroppedFiles(event.dataTransfer, {
|
||
logDropError
|
||
});
|
||
|
||
if (files.length > 0) {
|
||
this.uppy.log('[DropTarget] Files were dropped');
|
||
this.addFiles(files);
|
||
}
|
||
|
||
(_this$opts$onDrop = (_this$opts = this.opts).onDrop) == null ? void 0 : _this$opts$onDrop.call(_this$opts, event);
|
||
};
|
||
|
||
this.handleDragOver = event => {
|
||
var _this$opts$onDragOver, _this$opts2;
|
||
|
||
if (!this.isFileTransfer(event)) {
|
||
return;
|
||
}
|
||
|
||
event.preventDefault();
|
||
event.stopPropagation(); // Add a small (+) icon on drop
|
||
// (and prevent browsers from interpreting this as files being _moved_ into the browser,
|
||
// https://github.com/transloadit/uppy/issues/1978)
|
||
|
||
event.dataTransfer.dropEffect = 'copy';
|
||
clearTimeout(this.removeDragOverClassTimeout);
|
||
event.currentTarget.classList.add('uppy-is-drag-over');
|
||
this.setPluginState({
|
||
isDraggingOver: true
|
||
});
|
||
(_this$opts$onDragOver = (_this$opts2 = this.opts).onDragOver) == null ? void 0 : _this$opts$onDragOver.call(_this$opts2, event);
|
||
};
|
||
|
||
this.handleDragLeave = event => {
|
||
var _this$opts$onDragLeav, _this$opts3;
|
||
|
||
if (!this.isFileTransfer(event)) {
|
||
return;
|
||
}
|
||
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
const {
|
||
currentTarget
|
||
} = event;
|
||
clearTimeout(this.removeDragOverClassTimeout); // Timeout against flickering, this solution is taken from drag-drop library.
|
||
// Solution with 'pointer-events: none' didn't work across browsers.
|
||
|
||
this.removeDragOverClassTimeout = setTimeout(() => {
|
||
currentTarget.classList.remove('uppy-is-drag-over');
|
||
this.setPluginState({
|
||
isDraggingOver: false
|
||
});
|
||
}, 50);
|
||
(_this$opts$onDragLeav = (_this$opts3 = this.opts).onDragLeave) == null ? void 0 : _this$opts$onDragLeav.call(_this$opts3, event);
|
||
};
|
||
|
||
this.addListeners = () => {
|
||
const {
|
||
target
|
||
} = this.opts;
|
||
|
||
if (target instanceof Element) {
|
||
this.nodes = [target];
|
||
} else if (typeof target === 'string') {
|
||
this.nodes = toArray(document.querySelectorAll(target));
|
||
}
|
||
|
||
if (!this.nodes && !this.nodes.length > 0) {
|
||
throw new Error(`"${target}" does not match any HTML elements`);
|
||
}
|
||
|
||
this.nodes.forEach(node => {
|
||
node.addEventListener('dragover', this.handleDragOver, false);
|
||
node.addEventListener('dragleave', this.handleDragLeave, false);
|
||
node.addEventListener('drop', this.handleDrop, false);
|
||
});
|
||
};
|
||
|
||
this.removeListeners = () => {
|
||
if (this.nodes) {
|
||
this.nodes.forEach(node => {
|
||
node.removeEventListener('dragover', this.handleDragOver, false);
|
||
node.removeEventListener('dragleave', this.handleDragLeave, false);
|
||
node.removeEventListener('drop', this.handleDrop, false);
|
||
});
|
||
}
|
||
};
|
||
|
||
this.type = 'acquirer';
|
||
this.id = this.opts.id || 'DropTarget';
|
||
this.title = 'Drop Target'; // Default options
|
||
|
||
const defaultOpts = {
|
||
target: null
|
||
}; // Merge default options with the ones set by user
|
||
|
||
this.opts = { ...defaultOpts,
|
||
...opts
|
||
};
|
||
this.removeDragOverClassTimeout = null;
|
||
}
|
||
|
||
install() {
|
||
this.setPluginState({
|
||
isDraggingOver: false
|
||
});
|
||
this.addListeners();
|
||
}
|
||
|
||
uninstall() {
|
||
this.removeListeners();
|
||
}
|
||
|
||
}, _class.VERSION = "1.1.2", _temp);
|
||
},{"@uppy/core/lib/BasePlugin":15,"@uppy/utils/lib/getDroppedFiles":37,"@uppy/utils/lib/toArray":51}],25:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
var _publish = /*#__PURE__*/_classPrivateFieldLooseKey("publish");
|
||
|
||
/**
|
||
* Default store that keeps state in a simple object.
|
||
*/
|
||
class DefaultStore {
|
||
constructor() {
|
||
Object.defineProperty(this, _publish, {
|
||
value: _publish2
|
||
});
|
||
this.state = {};
|
||
this.callbacks = [];
|
||
}
|
||
|
||
getState() {
|
||
return this.state;
|
||
}
|
||
|
||
setState(patch) {
|
||
const prevState = { ...this.state
|
||
};
|
||
const nextState = { ...this.state,
|
||
...patch
|
||
};
|
||
this.state = nextState;
|
||
|
||
_classPrivateFieldLooseBase(this, _publish)[_publish](prevState, nextState, patch);
|
||
}
|
||
|
||
subscribe(listener) {
|
||
this.callbacks.push(listener);
|
||
return () => {
|
||
// Remove the listener.
|
||
this.callbacks.splice(this.callbacks.indexOf(listener), 1);
|
||
};
|
||
}
|
||
|
||
}
|
||
|
||
function _publish2() {
|
||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
args[_key] = arguments[_key];
|
||
}
|
||
|
||
this.callbacks.forEach(listener => {
|
||
listener(...args);
|
||
});
|
||
}
|
||
|
||
DefaultStore.VERSION = "2.0.3";
|
||
|
||
module.exports = function defaultStore() {
|
||
return new DefaultStore();
|
||
};
|
||
},{}],26:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Little AbortController proxy module so we can swap out the implementation easily later.
|
||
*/
|
||
exports.AbortController = globalThis.AbortController;
|
||
exports.AbortSignal = globalThis.AbortSignal;
|
||
|
||
exports.createAbortError = function (message) {
|
||
if (message === void 0) {
|
||
message = 'Aborted';
|
||
}
|
||
|
||
return new DOMException(message, 'AbortError');
|
||
};
|
||
},{}],27:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
var _emitter, _events;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
/**
|
||
* Create a wrapper around an event emitter with a `remove` method to remove
|
||
* all events that were added using the wrapped emitter.
|
||
*/
|
||
module.exports = (_emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter"), _events = /*#__PURE__*/_classPrivateFieldLooseKey("events"), class EventTracker {
|
||
constructor(emitter) {
|
||
Object.defineProperty(this, _emitter, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _events, {
|
||
writable: true,
|
||
value: []
|
||
});
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter] = emitter;
|
||
}
|
||
|
||
on(event, fn) {
|
||
_classPrivateFieldLooseBase(this, _events)[_events].push([event, fn]);
|
||
|
||
return _classPrivateFieldLooseBase(this, _emitter)[_emitter].on(event, fn);
|
||
}
|
||
|
||
remove() {
|
||
for (const [event, fn] of _classPrivateFieldLooseBase(this, _events)[_events].splice(0)) {
|
||
_classPrivateFieldLooseBase(this, _emitter)[_emitter].off(event, fn);
|
||
}
|
||
}
|
||
|
||
});
|
||
},{}],28:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
class NetworkError extends Error {
|
||
constructor(error, xhr) {
|
||
if (xhr === void 0) {
|
||
xhr = null;
|
||
}
|
||
|
||
super(`This looks like a network error, the endpoint might be blocked by an internet provider or a firewall.`);
|
||
this.cause = error;
|
||
this.isNetworkError = true;
|
||
this.request = xhr;
|
||
}
|
||
|
||
}
|
||
|
||
module.exports = NetworkError;
|
||
},{}],29:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
var _aliveTimer = /*#__PURE__*/_classPrivateFieldLooseKey("aliveTimer");
|
||
|
||
var _isDone = /*#__PURE__*/_classPrivateFieldLooseKey("isDone");
|
||
|
||
var _onTimedOut = /*#__PURE__*/_classPrivateFieldLooseKey("onTimedOut");
|
||
|
||
var _timeout = /*#__PURE__*/_classPrivateFieldLooseKey("timeout");
|
||
|
||
/**
|
||
* Helper to abort upload requests if there has not been any progress for `timeout` ms.
|
||
* Create an instance using `timer = new ProgressTimeout(10000, onTimeout)`
|
||
* Call `timer.progress()` to signal that there has been progress of any kind.
|
||
* Call `timer.done()` when the upload has completed.
|
||
*/
|
||
class ProgressTimeout {
|
||
constructor(timeout, timeoutHandler) {
|
||
Object.defineProperty(this, _aliveTimer, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _isDone, {
|
||
writable: true,
|
||
value: false
|
||
});
|
||
Object.defineProperty(this, _onTimedOut, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _timeout, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
_classPrivateFieldLooseBase(this, _timeout)[_timeout] = timeout;
|
||
_classPrivateFieldLooseBase(this, _onTimedOut)[_onTimedOut] = timeoutHandler;
|
||
}
|
||
|
||
progress() {
|
||
// Some browsers fire another progress event when the upload is
|
||
// cancelled, so we have to ignore progress after the timer was
|
||
// told to stop.
|
||
if (_classPrivateFieldLooseBase(this, _isDone)[_isDone]) return;
|
||
|
||
if (_classPrivateFieldLooseBase(this, _timeout)[_timeout] > 0) {
|
||
clearTimeout(_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer]);
|
||
_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer] = setTimeout(_classPrivateFieldLooseBase(this, _onTimedOut)[_onTimedOut], _classPrivateFieldLooseBase(this, _timeout)[_timeout]);
|
||
}
|
||
}
|
||
|
||
done() {
|
||
if (!_classPrivateFieldLooseBase(this, _isDone)[_isDone]) {
|
||
clearTimeout(_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer]);
|
||
_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer] = null;
|
||
_classPrivateFieldLooseBase(this, _isDone)[_isDone] = true;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
module.exports = ProgressTimeout;
|
||
},{}],30:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
function createCancelError() {
|
||
return new Error('Cancelled');
|
||
}
|
||
|
||
var _activeRequests = /*#__PURE__*/_classPrivateFieldLooseKey("activeRequests");
|
||
|
||
var _queuedHandlers = /*#__PURE__*/_classPrivateFieldLooseKey("queuedHandlers");
|
||
|
||
var _paused = /*#__PURE__*/_classPrivateFieldLooseKey("paused");
|
||
|
||
var _pauseTimer = /*#__PURE__*/_classPrivateFieldLooseKey("pauseTimer");
|
||
|
||
var _downLimit = /*#__PURE__*/_classPrivateFieldLooseKey("downLimit");
|
||
|
||
var _upperLimit = /*#__PURE__*/_classPrivateFieldLooseKey("upperLimit");
|
||
|
||
var _rateLimitingTimer = /*#__PURE__*/_classPrivateFieldLooseKey("rateLimitingTimer");
|
||
|
||
var _call = /*#__PURE__*/_classPrivateFieldLooseKey("call");
|
||
|
||
var _queueNext = /*#__PURE__*/_classPrivateFieldLooseKey("queueNext");
|
||
|
||
var _next = /*#__PURE__*/_classPrivateFieldLooseKey("next");
|
||
|
||
var _queue = /*#__PURE__*/_classPrivateFieldLooseKey("queue");
|
||
|
||
var _dequeue = /*#__PURE__*/_classPrivateFieldLooseKey("dequeue");
|
||
|
||
var _resume = /*#__PURE__*/_classPrivateFieldLooseKey("resume");
|
||
|
||
var _increaseLimit = /*#__PURE__*/_classPrivateFieldLooseKey("increaseLimit");
|
||
|
||
class RateLimitedQueue {
|
||
constructor(limit) {
|
||
Object.defineProperty(this, _dequeue, {
|
||
value: _dequeue2
|
||
});
|
||
Object.defineProperty(this, _queue, {
|
||
value: _queue2
|
||
});
|
||
Object.defineProperty(this, _next, {
|
||
value: _next2
|
||
});
|
||
Object.defineProperty(this, _queueNext, {
|
||
value: _queueNext2
|
||
});
|
||
Object.defineProperty(this, _call, {
|
||
value: _call2
|
||
});
|
||
Object.defineProperty(this, _activeRequests, {
|
||
writable: true,
|
||
value: 0
|
||
});
|
||
Object.defineProperty(this, _queuedHandlers, {
|
||
writable: true,
|
||
value: []
|
||
});
|
||
Object.defineProperty(this, _paused, {
|
||
writable: true,
|
||
value: false
|
||
});
|
||
Object.defineProperty(this, _pauseTimer, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _downLimit, {
|
||
writable: true,
|
||
value: 1
|
||
});
|
||
Object.defineProperty(this, _upperLimit, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _rateLimitingTimer, {
|
||
writable: true,
|
||
value: void 0
|
||
});
|
||
Object.defineProperty(this, _resume, {
|
||
writable: true,
|
||
value: () => this.resume()
|
||
});
|
||
Object.defineProperty(this, _increaseLimit, {
|
||
writable: true,
|
||
value: () => {
|
||
if (_classPrivateFieldLooseBase(this, _paused)[_paused]) {
|
||
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 0);
|
||
return;
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = this.limit;
|
||
this.limit = Math.ceil((_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] + _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]) / 2);
|
||
|
||
for (let i = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]; i <= this.limit; i++) {
|
||
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
|
||
}
|
||
|
||
if (_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] - _classPrivateFieldLooseBase(this, _downLimit)[_downLimit] > 3) {
|
||
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 2000);
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = Math.floor(_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] / 2);
|
||
}
|
||
}
|
||
});
|
||
|
||
if (typeof limit !== 'number' || limit === 0) {
|
||
this.limit = Infinity;
|
||
} else {
|
||
this.limit = limit;
|
||
}
|
||
}
|
||
|
||
run(fn, queueOptions) {
|
||
if (!_classPrivateFieldLooseBase(this, _paused)[_paused] && _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] < this.limit) {
|
||
return _classPrivateFieldLooseBase(this, _call)[_call](fn);
|
||
}
|
||
|
||
return _classPrivateFieldLooseBase(this, _queue)[_queue](fn, queueOptions);
|
||
}
|
||
|
||
wrapPromiseFunction(fn, queueOptions) {
|
||
var _this = this;
|
||
|
||
return function () {
|
||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
args[_key] = arguments[_key];
|
||
}
|
||
|
||
let queuedRequest;
|
||
const outerPromise = new Promise((resolve, reject) => {
|
||
queuedRequest = _this.run(() => {
|
||
let cancelError;
|
||
let innerPromise;
|
||
|
||
try {
|
||
innerPromise = Promise.resolve(fn(...args));
|
||
} catch (err) {
|
||
innerPromise = Promise.reject(err);
|
||
}
|
||
|
||
innerPromise.then(result => {
|
||
if (cancelError) {
|
||
reject(cancelError);
|
||
} else {
|
||
queuedRequest.done();
|
||
resolve(result);
|
||
}
|
||
}, err => {
|
||
if (cancelError) {
|
||
reject(cancelError);
|
||
} else {
|
||
queuedRequest.done();
|
||
reject(err);
|
||
}
|
||
});
|
||
return () => {
|
||
cancelError = createCancelError();
|
||
};
|
||
}, queueOptions);
|
||
});
|
||
|
||
outerPromise.abort = () => {
|
||
queuedRequest.abort();
|
||
};
|
||
|
||
return outerPromise;
|
||
};
|
||
}
|
||
|
||
resume() {
|
||
_classPrivateFieldLooseBase(this, _paused)[_paused] = false;
|
||
clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
|
||
|
||
for (let i = 0; i < this.limit; i++) {
|
||
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Freezes the queue for a while or indefinitely.
|
||
*
|
||
* @param {number | null } [duration] Duration for the pause to happen, in milliseconds.
|
||
* If omitted, the queue won't resume automatically.
|
||
*/
|
||
pause(duration) {
|
||
if (duration === void 0) {
|
||
duration = null;
|
||
}
|
||
|
||
_classPrivateFieldLooseBase(this, _paused)[_paused] = true;
|
||
clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
|
||
|
||
if (duration != null) {
|
||
_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer] = setTimeout(_classPrivateFieldLooseBase(this, _resume)[_resume], duration);
|
||
}
|
||
}
|
||
/**
|
||
* Pauses the queue for a duration, and lower the limit of concurrent requests
|
||
* when the queue resumes. When the queue resumes, it tries to progressively
|
||
* increase the limit in `this.#increaseLimit` until another call is made to
|
||
* `this.rateLimit`.
|
||
* Call this function when using the RateLimitedQueue for network requests and
|
||
* the remote server responds with 429 HTTP code.
|
||
*
|
||
* @param {number} duration in milliseconds.
|
||
*/
|
||
|
||
|
||
rateLimit(duration) {
|
||
clearTimeout(_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer]);
|
||
this.pause(duration);
|
||
|
||
if (this.limit > 1 && Number.isFinite(this.limit)) {
|
||
_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] = this.limit - 1;
|
||
this.limit = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit];
|
||
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], duration);
|
||
}
|
||
}
|
||
|
||
get isPaused() {
|
||
return _classPrivateFieldLooseBase(this, _paused)[_paused];
|
||
}
|
||
|
||
}
|
||
|
||
function _call2(fn) {
|
||
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] += 1;
|
||
let done = false;
|
||
let cancelActive;
|
||
|
||
try {
|
||
cancelActive = fn();
|
||
} catch (err) {
|
||
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
|
||
throw err;
|
||
}
|
||
|
||
return {
|
||
abort: () => {
|
||
if (done) return;
|
||
done = true;
|
||
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
|
||
cancelActive();
|
||
|
||
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
|
||
},
|
||
done: () => {
|
||
if (done) return;
|
||
done = true;
|
||
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
|
||
|
||
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
|
||
}
|
||
};
|
||
}
|
||
|
||
function _queueNext2() {
|
||
// Do it soon but not immediately, this allows clearing out the entire queue synchronously
|
||
// one by one without continuously _advancing_ it (and starting new tasks before immediately
|
||
// aborting them)
|
||
queueMicrotask(() => _classPrivateFieldLooseBase(this, _next)[_next]());
|
||
}
|
||
|
||
function _next2() {
|
||
if (_classPrivateFieldLooseBase(this, _paused)[_paused] || _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] >= this.limit) {
|
||
return;
|
||
}
|
||
|
||
if (_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].length === 0) {
|
||
return;
|
||
} // Dispatch the next request, and update the abort/done handlers
|
||
// so that cancelling it does the Right Thing (and doesn't just try
|
||
// to dequeue an already-running request).
|
||
|
||
|
||
const next = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].shift();
|
||
|
||
const handler = _classPrivateFieldLooseBase(this, _call)[_call](next.fn);
|
||
|
||
next.abort = handler.abort;
|
||
next.done = handler.done;
|
||
}
|
||
|
||
function _queue2(fn, options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
|
||
const handler = {
|
||
fn,
|
||
priority: options.priority || 0,
|
||
abort: () => {
|
||
_classPrivateFieldLooseBase(this, _dequeue)[_dequeue](handler);
|
||
},
|
||
done: () => {
|
||
throw new Error('Cannot mark a queued request as done: this indicates a bug');
|
||
}
|
||
};
|
||
|
||
const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].findIndex(other => {
|
||
return handler.priority > other.priority;
|
||
});
|
||
|
||
if (index === -1) {
|
||
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].push(handler);
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 0, handler);
|
||
}
|
||
|
||
return handler;
|
||
}
|
||
|
||
function _dequeue2(handler) {
|
||
const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].indexOf(handler);
|
||
|
||
if (index !== -1) {
|
||
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 1);
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
RateLimitedQueue,
|
||
internalRateLimitedQueue: Symbol('__queue')
|
||
};
|
||
},{}],31:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
var _apply;
|
||
|
||
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
||
|
||
var id = 0;
|
||
|
||
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
||
|
||
const has = require('./hasProperty');
|
||
|
||
function insertReplacement(source, rx, replacement) {
|
||
const newParts = [];
|
||
source.forEach(chunk => {
|
||
// When the source contains multiple placeholders for interpolation,
|
||
// we should ignore chunks that are not strings, because those
|
||
// can be JSX objects and will be otherwise incorrectly turned into strings.
|
||
// Without this condition we’d get this: [object Object] hello [object Object] my <button>
|
||
if (typeof chunk !== 'string') {
|
||
return newParts.push(chunk);
|
||
}
|
||
|
||
return rx[Symbol.split](chunk).forEach((raw, i, list) => {
|
||
if (raw !== '') {
|
||
newParts.push(raw);
|
||
} // Interlace with the `replacement` value
|
||
|
||
|
||
if (i < list.length - 1) {
|
||
newParts.push(replacement);
|
||
}
|
||
});
|
||
});
|
||
return newParts;
|
||
}
|
||
/**
|
||
* Takes a string with placeholder variables like `%{smart_count} file selected`
|
||
* and replaces it with values from options `{smart_count: 5}`
|
||
*
|
||
* @license https://github.com/airbnb/polyglot.js/blob/master/LICENSE
|
||
* taken from https://github.com/airbnb/polyglot.js/blob/master/lib/polyglot.js#L299
|
||
*
|
||
* @param {string} phrase that needs interpolation, with placeholders
|
||
* @param {object} options with values that will be used to replace placeholders
|
||
* @returns {any[]} interpolated
|
||
*/
|
||
|
||
|
||
function interpolate(phrase, options) {
|
||
const dollarRegex = /\$/g;
|
||
const dollarBillsYall = '$$$$';
|
||
let interpolated = [phrase];
|
||
if (options == null) return interpolated;
|
||
|
||
for (const arg of Object.keys(options)) {
|
||
if (arg !== '_') {
|
||
// Ensure replacement value is escaped to prevent special $-prefixed
|
||
// regex replace tokens. the "$$$$" is needed because each "$" needs to
|
||
// be escaped with "$" itself, and we need two in the resulting output.
|
||
let replacement = options[arg];
|
||
|
||
if (typeof replacement === 'string') {
|
||
replacement = dollarRegex[Symbol.replace](replacement, dollarBillsYall);
|
||
} // We create a new `RegExp` each time instead of using a more-efficient
|
||
// string replace so that the same argument can be replaced multiple times
|
||
// in the same phrase.
|
||
|
||
|
||
interpolated = insertReplacement(interpolated, new RegExp(`%\\{${arg}\\}`, 'g'), replacement);
|
||
}
|
||
}
|
||
|
||
return interpolated;
|
||
}
|
||
/**
|
||
* Translates strings with interpolation & pluralization support.
|
||
* Extensible with custom dictionaries and pluralization functions.
|
||
*
|
||
* Borrows heavily from and inspired by Polyglot https://github.com/airbnb/polyglot.js,
|
||
* basically a stripped-down version of it. Differences: pluralization functions are not hardcoded
|
||
* and can be easily added among with dictionaries, nested objects are used for pluralization
|
||
* as opposed to `||||` delimeter
|
||
*
|
||
* Usage example: `translator.translate('files_chosen', {smart_count: 3})`
|
||
*/
|
||
|
||
|
||
module.exports = (_apply = /*#__PURE__*/_classPrivateFieldLooseKey("apply"), class Translator {
|
||
/**
|
||
* @param {object|Array<object>} locales - locale or list of locales.
|
||
*/
|
||
constructor(locales) {
|
||
Object.defineProperty(this, _apply, {
|
||
value: _apply2
|
||
});
|
||
this.locale = {
|
||
strings: {},
|
||
|
||
pluralize(n) {
|
||
if (n === 1) {
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
};
|
||
|
||
if (Array.isArray(locales)) {
|
||
locales.forEach(_classPrivateFieldLooseBase(this, _apply)[_apply], this);
|
||
} else {
|
||
_classPrivateFieldLooseBase(this, _apply)[_apply](locales);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Public translate method
|
||
*
|
||
* @param {string} key
|
||
* @param {object} options with values that will be used later to replace placeholders in string
|
||
* @returns {string} translated (and interpolated)
|
||
*/
|
||
translate(key, options) {
|
||
return this.translateArray(key, options).join('');
|
||
}
|
||
/**
|
||
* Get a translation and return the translated and interpolated parts as an array.
|
||
*
|
||
* @param {string} key
|
||
* @param {object} options with values that will be used to replace placeholders
|
||
* @returns {Array} The translated and interpolated parts, in order.
|
||
*/
|
||
|
||
|
||
translateArray(key, options) {
|
||
if (!has(this.locale.strings, key)) {
|
||
throw new Error(`missing string: ${key}`);
|
||
}
|
||
|
||
const string = this.locale.strings[key];
|
||
const hasPluralForms = typeof string === 'object';
|
||
|
||
if (hasPluralForms) {
|
||
if (options && typeof options.smart_count !== 'undefined') {
|
||
const plural = this.locale.pluralize(options.smart_count);
|
||
return interpolate(string[plural], options);
|
||
}
|
||
|
||
throw new Error('Attempted to use a string with plural forms, but no value was given for %{smart_count}');
|
||
}
|
||
|
||
return interpolate(string, options);
|
||
}
|
||
|
||
});
|
||
|
||
function _apply2(locale) {
|
||
if (!(locale != null && locale.strings)) {
|
||
return;
|
||
}
|
||
|
||
const prevLocale = this.locale;
|
||
this.locale = { ...prevLocale,
|
||
strings: { ...prevLocale.strings,
|
||
...locale.strings
|
||
}
|
||
};
|
||
this.locale.pluralize = locale.pluralize || prevLocale.pluralize;
|
||
}
|
||
},{"./hasProperty":46}],32:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const {
|
||
createAbortError
|
||
} = require('./AbortController');
|
||
/**
|
||
* Return a Promise that resolves after `ms` milliseconds.
|
||
*
|
||
* @param {number} ms - Number of milliseconds to wait.
|
||
* @param {{ signal?: AbortSignal }} [opts] - An abort signal that can be used to cancel the delay early.
|
||
* @returns {Promise<void>} A Promise that resolves after the given amount of `ms`.
|
||
*/
|
||
|
||
|
||
module.exports = function delay(ms, opts) {
|
||
return new Promise((resolve, reject) => {
|
||
var _opts$signal, _opts$signal2;
|
||
|
||
if (opts != null && (_opts$signal = opts.signal) != null && _opts$signal.aborted) {
|
||
return reject(createAbortError());
|
||
}
|
||
|
||
const timeout = setTimeout(() => {
|
||
cleanup(); // eslint-disable-line no-use-before-define
|
||
|
||
resolve();
|
||
}, ms);
|
||
|
||
function onabort() {
|
||
clearTimeout(timeout);
|
||
cleanup(); // eslint-disable-line no-use-before-define
|
||
|
||
reject(createAbortError());
|
||
}
|
||
|
||
opts == null ? void 0 : (_opts$signal2 = opts.signal) == null ? void 0 : _opts$signal2.addEventListener('abort', onabort);
|
||
|
||
function cleanup() {
|
||
var _opts$signal3;
|
||
|
||
opts == null ? void 0 : (_opts$signal3 = opts.signal) == null ? void 0 : _opts$signal3.removeEventListener('abort', onabort);
|
||
}
|
||
|
||
return undefined;
|
||
});
|
||
};
|
||
},{"./AbortController":26}],33:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const throttle = require('lodash.throttle');
|
||
|
||
function emitSocketProgress(uploader, progressData, file) {
|
||
const {
|
||
progress,
|
||
bytesUploaded,
|
||
bytesTotal
|
||
} = progressData;
|
||
|
||
if (progress) {
|
||
uploader.uppy.log(`Upload progress: ${progress}`);
|
||
uploader.uppy.emit('upload-progress', file, {
|
||
uploader,
|
||
bytesUploaded,
|
||
bytesTotal
|
||
});
|
||
}
|
||
}
|
||
|
||
module.exports = throttle(emitSocketProgress, 300, {
|
||
leading: true,
|
||
trailing: true
|
||
});
|
||
},{"lodash.throttle":54}],34:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const NetworkError = require('./NetworkError');
|
||
/**
|
||
* Wrapper around window.fetch that throws a NetworkError when appropriate
|
||
*/
|
||
|
||
|
||
module.exports = function fetchWithNetworkError() {
|
||
return fetch(...arguments).catch(err => {
|
||
if (err.name === 'AbortError') {
|
||
throw err;
|
||
} else {
|
||
throw new NetworkError(err);
|
||
}
|
||
});
|
||
};
|
||
},{"./NetworkError":28}],35:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const isDOMElement = require('./isDOMElement');
|
||
/**
|
||
* Find a DOM element.
|
||
*
|
||
* @param {Node|string} element
|
||
* @returns {Node|null}
|
||
*/
|
||
|
||
|
||
module.exports = function findDOMElement(element, context) {
|
||
if (context === void 0) {
|
||
context = document;
|
||
}
|
||
|
||
if (typeof element === 'string') {
|
||
return context.querySelector(element);
|
||
}
|
||
|
||
if (isDOMElement(element)) {
|
||
return element;
|
||
}
|
||
|
||
return null;
|
||
};
|
||
},{"./isDOMElement":47}],36:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
function encodeCharacter(character) {
|
||
return character.charCodeAt(0).toString(32);
|
||
}
|
||
|
||
function encodeFilename(name) {
|
||
let suffix = '';
|
||
return name.replace(/[^A-Z0-9]/ig, character => {
|
||
suffix += `-${encodeCharacter(character)}`;
|
||
return '/';
|
||
}) + suffix;
|
||
}
|
||
/**
|
||
* Takes a file object and turns it into fileID, by converting file.name to lowercase,
|
||
* removing extra characters and adding type, size and lastModified
|
||
*
|
||
* @param {object} file
|
||
* @returns {string} the fileID
|
||
*/
|
||
|
||
|
||
module.exports = function generateFileID(file) {
|
||
// It's tempting to do `[items].filter(Boolean).join('-')` here, but that
|
||
// is slower! simple string concatenation is fast
|
||
let id = 'uppy';
|
||
|
||
if (typeof file.name === 'string') {
|
||
id += `-${encodeFilename(file.name.toLowerCase())}`;
|
||
}
|
||
|
||
if (file.type !== undefined) {
|
||
id += `-${file.type}`;
|
||
}
|
||
|
||
if (file.meta && typeof file.meta.relativePath === 'string') {
|
||
id += `-${encodeFilename(file.meta.relativePath.toLowerCase())}`;
|
||
}
|
||
|
||
if (file.data.size !== undefined) {
|
||
id += `-${file.data.size}`;
|
||
}
|
||
|
||
if (file.data.lastModified !== undefined) {
|
||
id += `-${file.data.lastModified}`;
|
||
}
|
||
|
||
return id;
|
||
};
|
||
},{}],37:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const webkitGetAsEntryApi = require('./utils/webkitGetAsEntryApi/index');
|
||
|
||
const fallbackApi = require('./utils/fallbackApi');
|
||
/**
|
||
* Returns a promise that resolves to the array of dropped files (if a folder is
|
||
* dropped, and browser supports folder parsing - promise resolves to the flat
|
||
* array of all files in all directories).
|
||
* Each file has .relativePath prop appended to it (e.g. "/docs/Prague/ticket_from_prague_to_ufa.pdf")
|
||
* if browser supports it. Otherwise it's undefined.
|
||
*
|
||
* @param {DataTransfer} dataTransfer
|
||
* @param {Function} logDropError - a function that's called every time some
|
||
* folder or some file error out (e.g. because of the folder name being too long
|
||
* on Windows). Notice that resulting promise will always be resolved anyway.
|
||
*
|
||
* @returns {Promise} - Array<File>
|
||
*/
|
||
|
||
|
||
module.exports = function getDroppedFiles(dataTransfer, _temp) {
|
||
var _dataTransfer$items;
|
||
|
||
let {
|
||
logDropError = () => {}
|
||
} = _temp === void 0 ? {} : _temp;
|
||
|
||
// Get all files from all subdirs. Works (at least) in Chrome, Mozilla, and Safari
|
||
if ((_dataTransfer$items = dataTransfer.items) != null && _dataTransfer$items[0] && 'webkitGetAsEntry' in dataTransfer.items[0]) {
|
||
return webkitGetAsEntryApi(dataTransfer, logDropError); // Otherwise just return all first-order files
|
||
}
|
||
|
||
return fallbackApi(dataTransfer);
|
||
};
|
||
},{"./utils/fallbackApi":38,"./utils/webkitGetAsEntryApi/index":41}],38:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const toArray = require('../../toArray'); // .files fallback, should be implemented in any browser
|
||
|
||
|
||
module.exports = function fallbackApi(dataTransfer) {
|
||
const files = toArray(dataTransfer.files);
|
||
return Promise.resolve(files);
|
||
};
|
||
},{"../../toArray":51}],39:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Recursive function, calls the original callback() when the directory is entirely parsed.
|
||
*
|
||
* @param {FileSystemDirectoryReader} directoryReader
|
||
* @param {Array} oldEntries
|
||
* @param {Function} logDropError
|
||
* @param {Function} callback - called with ([ all files and directories in that directoryReader ])
|
||
*/
|
||
module.exports = function getFilesAndDirectoriesFromDirectory(directoryReader, oldEntries, logDropError, _ref) {
|
||
let {
|
||
onSuccess
|
||
} = _ref;
|
||
directoryReader.readEntries(entries => {
|
||
const newEntries = [...oldEntries, ...entries]; // According to the FileSystem API spec, getFilesAndDirectoriesFromDirectory()
|
||
// must be called until it calls the onSuccess with an empty array.
|
||
|
||
if (entries.length) {
|
||
setTimeout(() => {
|
||
getFilesAndDirectoriesFromDirectory(directoryReader, newEntries, logDropError, {
|
||
onSuccess
|
||
});
|
||
}, 0); // Done iterating this particular directory
|
||
} else {
|
||
onSuccess(newEntries);
|
||
}
|
||
}, // Make sure we resolve on error anyway, it's fine if only one directory couldn't be parsed!
|
||
error => {
|
||
logDropError(error);
|
||
onSuccess(oldEntries);
|
||
});
|
||
};
|
||
},{}],40:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Get the relative path from the FileEntry#fullPath, because File#webkitRelativePath is always '', at least onDrop.
|
||
*
|
||
* @param {FileEntry} fileEntry
|
||
*
|
||
* @returns {string|null} - if file is not in a folder - return null (this is to
|
||
* be consistent with .relativePath-s of files selected from My Device). If file
|
||
* is in a folder - return its fullPath, e.g. '/simpsons/hi.jpeg'.
|
||
*/
|
||
module.exports = function getRelativePath(fileEntry) {
|
||
// fileEntry.fullPath - "/simpsons/hi.jpeg" or undefined (for browsers that don't support it)
|
||
// fileEntry.name - "hi.jpeg"
|
||
if (!fileEntry.fullPath || fileEntry.fullPath === `/${fileEntry.name}`) {
|
||
return null;
|
||
}
|
||
|
||
return fileEntry.fullPath;
|
||
};
|
||
},{}],41:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const toArray = require('../../../toArray');
|
||
|
||
const getRelativePath = require('./getRelativePath');
|
||
|
||
const getFilesAndDirectoriesFromDirectory = require('./getFilesAndDirectoriesFromDirectory');
|
||
|
||
module.exports = function webkitGetAsEntryApi(dataTransfer, logDropError) {
|
||
const files = [];
|
||
const rootPromises = [];
|
||
/**
|
||
* Returns a resolved promise, when :files array is enhanced
|
||
*
|
||
* @param {(FileSystemFileEntry|FileSystemDirectoryEntry)} entry
|
||
* @returns {Promise} - empty promise that resolves when :files is enhanced with a file
|
||
*/
|
||
|
||
const createPromiseToAddFileOrParseDirectory = entry => new Promise(resolve => {
|
||
// This is a base call
|
||
if (entry.isFile) {
|
||
// Creates a new File object which can be used to read the file.
|
||
entry.file(file => {
|
||
// eslint-disable-next-line no-param-reassign
|
||
file.relativePath = getRelativePath(entry);
|
||
files.push(file);
|
||
resolve();
|
||
}, // Make sure we resolve on error anyway, it's fine if only one file couldn't be read!
|
||
error => {
|
||
logDropError(error);
|
||
resolve();
|
||
}); // This is a recursive call
|
||
} else if (entry.isDirectory) {
|
||
const directoryReader = entry.createReader();
|
||
getFilesAndDirectoriesFromDirectory(directoryReader, [], logDropError, {
|
||
onSuccess: entries => resolve(Promise.all(entries.map(createPromiseToAddFileOrParseDirectory)))
|
||
});
|
||
}
|
||
}); // For each dropped item, - make sure it's a file/directory, and start deepening in!
|
||
|
||
|
||
toArray(dataTransfer.items).forEach(item => {
|
||
const entry = item.webkitGetAsEntry(); // :entry can be null when we drop the url e.g.
|
||
|
||
if (entry) {
|
||
rootPromises.push(createPromiseToAddFileOrParseDirectory(entry));
|
||
}
|
||
});
|
||
return Promise.all(rootPromises).then(() => files);
|
||
};
|
||
},{"../../../toArray":51,"./getFilesAndDirectoriesFromDirectory":39,"./getRelativePath":40}],42:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Takes a full filename string and returns an object {name, extension}
|
||
*
|
||
* @param {string} fullFileName
|
||
* @returns {object} {name, extension}
|
||
*/
|
||
module.exports = function getFileNameAndExtension(fullFileName) {
|
||
const lastDot = fullFileName.lastIndexOf('.'); // these count as no extension: "no-dot", "trailing-dot."
|
||
|
||
if (lastDot === -1 || lastDot === fullFileName.length - 1) {
|
||
return {
|
||
name: fullFileName,
|
||
extension: undefined
|
||
};
|
||
}
|
||
|
||
return {
|
||
name: fullFileName.slice(0, lastDot),
|
||
extension: fullFileName.slice(lastDot + 1)
|
||
};
|
||
};
|
||
},{}],43:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
const getFileNameAndExtension = require('./getFileNameAndExtension');
|
||
|
||
const mimeTypes = require('./mimeTypes');
|
||
|
||
module.exports = function getFileType(file) {
|
||
var _getFileNameAndExtens;
|
||
|
||
if (file.type) return file.type;
|
||
const fileExtension = file.name ? (_getFileNameAndExtens = getFileNameAndExtension(file.name).extension) == null ? void 0 : _getFileNameAndExtens.toLowerCase() : null;
|
||
|
||
if (fileExtension && fileExtension in mimeTypes) {
|
||
// else, see if we can map extension to a mime type
|
||
return mimeTypes[fileExtension];
|
||
} // if all fails, fall back to a generic byte stream type
|
||
|
||
|
||
return 'application/octet-stream';
|
||
};
|
||
},{"./getFileNameAndExtension":42,"./mimeTypes":49}],44:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
module.exports = function getSocketHost(url) {
|
||
// get the host domain
|
||
const regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/i;
|
||
const host = regex.exec(url)[1];
|
||
const socketProtocol = /^http:\/\//i.test(url) ? 'ws' : 'wss';
|
||
return `${socketProtocol}://${host}`;
|
||
};
|
||
},{}],45:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Adds zero to strings shorter than two characters.
|
||
*
|
||
* @param {number} number
|
||
* @returns {string}
|
||
*/
|
||
function pad(number) {
|
||
return number < 10 ? `0${number}` : number.toString();
|
||
}
|
||
/**
|
||
* Returns a timestamp in the format of `hours:minutes:seconds`
|
||
*/
|
||
|
||
|
||
module.exports = function getTimeStamp() {
|
||
const date = new Date();
|
||
const hours = pad(date.getHours());
|
||
const minutes = pad(date.getMinutes());
|
||
const seconds = pad(date.getSeconds());
|
||
return `${hours}:${minutes}:${seconds}`;
|
||
};
|
||
},{}],46:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
module.exports = function has(object, key) {
|
||
return Object.prototype.hasOwnProperty.call(object, key);
|
||
};
|
||
},{}],47:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Check if an object is a DOM element. Duck-typing based on `nodeType`.
|
||
*
|
||
* @param {*} obj
|
||
*/
|
||
module.exports = function isDOMElement(obj) {
|
||
return (obj == null ? void 0 : obj.nodeType) === Node.ELEMENT_NODE;
|
||
};
|
||
},{}],48:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
function isNetworkError(xhr) {
|
||
if (!xhr) {
|
||
return false;
|
||
}
|
||
|
||
return xhr.readyState !== 0 && xhr.readyState !== 4 || xhr.status === 0;
|
||
}
|
||
|
||
module.exports = isNetworkError;
|
||
},{}],49:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
// ___Why not add the mime-types package?
|
||
// It's 19.7kB gzipped, and we only need mime types for well-known extensions (for file previews).
|
||
// ___Where to take new extensions from?
|
||
// https://github.com/jshttp/mime-db/blob/master/db.json
|
||
module.exports = {
|
||
md: 'text/markdown',
|
||
markdown: 'text/markdown',
|
||
mp4: 'video/mp4',
|
||
mp3: 'audio/mp3',
|
||
svg: 'image/svg+xml',
|
||
jpg: 'image/jpeg',
|
||
png: 'image/png',
|
||
gif: 'image/gif',
|
||
heic: 'image/heic',
|
||
heif: 'image/heif',
|
||
yaml: 'text/yaml',
|
||
yml: 'text/yaml',
|
||
csv: 'text/csv',
|
||
tsv: 'text/tab-separated-values',
|
||
tab: 'text/tab-separated-values',
|
||
avi: 'video/x-msvideo',
|
||
mks: 'video/x-matroska',
|
||
mkv: 'video/x-matroska',
|
||
mov: 'video/quicktime',
|
||
doc: 'application/msword',
|
||
docm: 'application/vnd.ms-word.document.macroenabled.12',
|
||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||
dot: 'application/msword',
|
||
dotm: 'application/vnd.ms-word.template.macroenabled.12',
|
||
dotx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||
xla: 'application/vnd.ms-excel',
|
||
xlam: 'application/vnd.ms-excel.addin.macroenabled.12',
|
||
xlc: 'application/vnd.ms-excel',
|
||
xlf: 'application/x-xliff+xml',
|
||
xlm: 'application/vnd.ms-excel',
|
||
xls: 'application/vnd.ms-excel',
|
||
xlsb: 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
|
||
xlsm: 'application/vnd.ms-excel.sheet.macroenabled.12',
|
||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||
xlt: 'application/vnd.ms-excel',
|
||
xltm: 'application/vnd.ms-excel.template.macroenabled.12',
|
||
xltx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||
xlw: 'application/vnd.ms-excel',
|
||
txt: 'text/plain',
|
||
text: 'text/plain',
|
||
conf: 'text/plain',
|
||
log: 'text/plain',
|
||
pdf: 'application/pdf',
|
||
zip: 'application/zip',
|
||
'7z': 'application/x-7z-compressed',
|
||
rar: 'application/x-rar-compressed',
|
||
tar: 'application/x-tar',
|
||
gz: 'application/gzip',
|
||
dmg: 'application/x-apple-diskimage'
|
||
};
|
||
},{}],50:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
module.exports = function settle(promises) {
|
||
const resolutions = [];
|
||
const rejections = [];
|
||
|
||
function resolved(value) {
|
||
resolutions.push(value);
|
||
}
|
||
|
||
function rejected(error) {
|
||
rejections.push(error);
|
||
}
|
||
|
||
const wait = Promise.all(promises.map(promise => promise.then(resolved, rejected)));
|
||
return wait.then(() => {
|
||
return {
|
||
successful: resolutions,
|
||
failed: rejections
|
||
};
|
||
});
|
||
};
|
||
},{}],51:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
/**
|
||
* Converts list into array
|
||
*/
|
||
module.exports = Array.from;
|
||
},{}],52:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
var _class, _temp;
|
||
|
||
const BasePlugin = require('@uppy/core/lib/BasePlugin');
|
||
|
||
const {
|
||
nanoid
|
||
} = require('nanoid/non-secure');
|
||
|
||
const {
|
||
Provider,
|
||
RequestClient,
|
||
Socket
|
||
} = require('@uppy/companion-client');
|
||
|
||
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 {
|
||
RateLimitedQueue,
|
||
internalRateLimitedQueue
|
||
} = require('@uppy/utils/lib/RateLimitedQueue');
|
||
|
||
const NetworkError = require('@uppy/utils/lib/NetworkError');
|
||
|
||
const isNetworkError = require('@uppy/utils/lib/isNetworkError');
|
||
|
||
const locale = require('./locale');
|
||
|
||
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;
|
||
}
|
||
|
||
module.exports = (_temp = _class = 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: '',
|
||
|
||
/**
|
||
* @typedef respObj
|
||
* @property {string} responseText
|
||
* @property {number} status
|
||
* @property {string} statusText
|
||
* @property {object.<string, string>} headers
|
||
*
|
||
* @param {string} responseText the response body string
|
||
* @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
|
||
*/
|
||
getResponseData(responseText) {
|
||
let parsedResponse = {};
|
||
|
||
try {
|
||
parsedResponse = JSON.parse(responseText);
|
||
} catch (err) {
|
||
uppy.log(err);
|
||
}
|
||
|
||
return parsedResponse;
|
||
},
|
||
|
||
/**
|
||
*
|
||
* @param {string} responseText 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 (internalRateLimitedQueue in this.opts) {
|
||
this.requests = this.opts[internalRateLimitedQueue];
|
||
} else {
|
||
this.requests = new 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);
|
||
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 = 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', ev => {
|
||
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(ev.target.status, xhr.responseText, xhr)) {
|
||
const body = opts.getResponseData(xhr.responseText, xhr);
|
||
const uploadURL = body[opts.responseUrlFieldName];
|
||
const uploadResp = {
|
||
status: ev.target.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: ev.target.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;
|
||
}
|
||
|
||
const 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, () => {
|
||
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 ? Provider : 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 Socket({
|
||
target: `${host}/api/${token}`,
|
||
autoOpen: false
|
||
});
|
||
this.uploaderEvents[file.id] = new EventTracker(this.uppy);
|
||
this.onFileRemove(file.id, () => {
|
||
socket.send('cancel', {});
|
||
queuedRequest.abort();
|
||
resolve(`upload ${file.id} was removed`);
|
||
});
|
||
this.onCancelAll(file.id, () => {
|
||
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);
|
||
});
|
||
const 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 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);
|
||
});
|
||
|
||
const emitError = error => {
|
||
files.forEach(file => {
|
||
this.uppy.emit('upload-error', file, 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', () => {
|
||
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, cb) {
|
||
this.uploaderEvents[fileID].on('cancel-all', () => {
|
||
if (!this.uppy.getFile(fileID)) return;
|
||
cb();
|
||
});
|
||
}
|
||
|
||
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[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);
|
||
}
|
||
|
||
}, _class.VERSION = "2.0.7", _temp);
|
||
},{"./locale":53,"@uppy/companion-client":13,"@uppy/core/lib/BasePlugin":15,"@uppy/utils/lib/EventTracker":27,"@uppy/utils/lib/NetworkError":28,"@uppy/utils/lib/ProgressTimeout":29,"@uppy/utils/lib/RateLimitedQueue":30,"@uppy/utils/lib/emitSocketProgress":33,"@uppy/utils/lib/getSocketHost":44,"@uppy/utils/lib/isNetworkError":48,"@uppy/utils/lib/settle":50,"nanoid/non-secure":57}],53:[function(require,module,exports){
|
||
"use strict";
|
||
|
||
module.exports = {
|
||
strings: {
|
||
// Shown in the Informer if an upload is being canceled because it stalled for too long.
|
||
timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
|
||
}
|
||
};
|
||
},{}],54:[function(require,module,exports){
|
||
(function (global){(function (){
|
||
/**
|
||
* lodash (Custom Build) <https://lodash.com/>
|
||
* Build: `lodash modularize exports="npm" -o ./`
|
||
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
|
||
* Released under MIT license <https://lodash.com/license>
|
||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||
*/
|
||
|
||
/** Used as the `TypeError` message for "Functions" methods. */
|
||
var FUNC_ERROR_TEXT = 'Expected a function';
|
||
|
||
/** Used as references for various `Number` constants. */
|
||
var NAN = 0 / 0;
|
||
|
||
/** `Object#toString` result references. */
|
||
var symbolTag = '[object Symbol]';
|
||
|
||
/** Used to match leading and trailing whitespace. */
|
||
var reTrim = /^\s+|\s+$/g;
|
||
|
||
/** Used to detect bad signed hexadecimal string values. */
|
||
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
|
||
|
||
/** Used to detect binary string values. */
|
||
var reIsBinary = /^0b[01]+$/i;
|
||
|
||
/** Used to detect octal string values. */
|
||
var reIsOctal = /^0o[0-7]+$/i;
|
||
|
||
/** Built-in method references without a dependency on `root`. */
|
||
var freeParseInt = parseInt;
|
||
|
||
/** Detect free variable `global` from Node.js. */
|
||
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||
|
||
/** Detect free variable `self`. */
|
||
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
|
||
|
||
/** Used as a reference to the global object. */
|
||
var root = freeGlobal || freeSelf || Function('return this')();
|
||
|
||
/** Used for built-in method references. */
|
||
var objectProto = Object.prototype;
|
||
|
||
/**
|
||
* Used to resolve the
|
||
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
||
* of values.
|
||
*/
|
||
var objectToString = objectProto.toString;
|
||
|
||
/* Built-in method references for those with the same name as other `lodash` methods. */
|
||
var nativeMax = Math.max,
|
||
nativeMin = Math.min;
|
||
|
||
/**
|
||
* Gets the timestamp of the number of milliseconds that have elapsed since
|
||
* the Unix epoch (1 January 1970 00:00:00 UTC).
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 2.4.0
|
||
* @category Date
|
||
* @returns {number} Returns the timestamp.
|
||
* @example
|
||
*
|
||
* _.defer(function(stamp) {
|
||
* console.log(_.now() - stamp);
|
||
* }, _.now());
|
||
* // => Logs the number of milliseconds it took for the deferred invocation.
|
||
*/
|
||
var now = function() {
|
||
return root.Date.now();
|
||
};
|
||
|
||
/**
|
||
* Creates a debounced function that delays invoking `func` until after `wait`
|
||
* milliseconds have elapsed since the last time the debounced function was
|
||
* invoked. The debounced function comes with a `cancel` method to cancel
|
||
* delayed `func` invocations and a `flush` method to immediately invoke them.
|
||
* Provide `options` to indicate whether `func` should be invoked on the
|
||
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
|
||
* with the last arguments provided to the debounced function. Subsequent
|
||
* calls to the debounced function return the result of the last `func`
|
||
* invocation.
|
||
*
|
||
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||
* invoked on the trailing edge of the timeout only if the debounced function
|
||
* is invoked more than once during the `wait` timeout.
|
||
*
|
||
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||
*
|
||
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||
* for details over the differences between `_.debounce` and `_.throttle`.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Function
|
||
* @param {Function} func The function to debounce.
|
||
* @param {number} [wait=0] The number of milliseconds to delay.
|
||
* @param {Object} [options={}] The options object.
|
||
* @param {boolean} [options.leading=false]
|
||
* Specify invoking on the leading edge of the timeout.
|
||
* @param {number} [options.maxWait]
|
||
* The maximum time `func` is allowed to be delayed before it's invoked.
|
||
* @param {boolean} [options.trailing=true]
|
||
* Specify invoking on the trailing edge of the timeout.
|
||
* @returns {Function} Returns the new debounced function.
|
||
* @example
|
||
*
|
||
* // Avoid costly calculations while the window size is in flux.
|
||
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
|
||
*
|
||
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
|
||
* jQuery(element).on('click', _.debounce(sendMail, 300, {
|
||
* 'leading': true,
|
||
* 'trailing': false
|
||
* }));
|
||
*
|
||
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
|
||
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
|
||
* var source = new EventSource('/stream');
|
||
* jQuery(source).on('message', debounced);
|
||
*
|
||
* // Cancel the trailing debounced invocation.
|
||
* jQuery(window).on('popstate', debounced.cancel);
|
||
*/
|
||
function debounce(func, wait, options) {
|
||
var lastArgs,
|
||
lastThis,
|
||
maxWait,
|
||
result,
|
||
timerId,
|
||
lastCallTime,
|
||
lastInvokeTime = 0,
|
||
leading = false,
|
||
maxing = false,
|
||
trailing = true;
|
||
|
||
if (typeof func != 'function') {
|
||
throw new TypeError(FUNC_ERROR_TEXT);
|
||
}
|
||
wait = toNumber(wait) || 0;
|
||
if (isObject(options)) {
|
||
leading = !!options.leading;
|
||
maxing = 'maxWait' in options;
|
||
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
|
||
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
||
}
|
||
|
||
function invokeFunc(time) {
|
||
var args = lastArgs,
|
||
thisArg = lastThis;
|
||
|
||
lastArgs = lastThis = undefined;
|
||
lastInvokeTime = time;
|
||
result = func.apply(thisArg, args);
|
||
return result;
|
||
}
|
||
|
||
function leadingEdge(time) {
|
||
// Reset any `maxWait` timer.
|
||
lastInvokeTime = time;
|
||
// Start the timer for the trailing edge.
|
||
timerId = setTimeout(timerExpired, wait);
|
||
// Invoke the leading edge.
|
||
return leading ? invokeFunc(time) : result;
|
||
}
|
||
|
||
function remainingWait(time) {
|
||
var timeSinceLastCall = time - lastCallTime,
|
||
timeSinceLastInvoke = time - lastInvokeTime,
|
||
result = wait - timeSinceLastCall;
|
||
|
||
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
|
||
}
|
||
|
||
function shouldInvoke(time) {
|
||
var timeSinceLastCall = time - lastCallTime,
|
||
timeSinceLastInvoke = time - lastInvokeTime;
|
||
|
||
// Either this is the first call, activity has stopped and we're at the
|
||
// trailing edge, the system time has gone backwards and we're treating
|
||
// it as the trailing edge, or we've hit the `maxWait` limit.
|
||
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
|
||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
|
||
}
|
||
|
||
function timerExpired() {
|
||
var time = now();
|
||
if (shouldInvoke(time)) {
|
||
return trailingEdge(time);
|
||
}
|
||
// Restart the timer.
|
||
timerId = setTimeout(timerExpired, remainingWait(time));
|
||
}
|
||
|
||
function trailingEdge(time) {
|
||
timerId = undefined;
|
||
|
||
// Only invoke if we have `lastArgs` which means `func` has been
|
||
// debounced at least once.
|
||
if (trailing && lastArgs) {
|
||
return invokeFunc(time);
|
||
}
|
||
lastArgs = lastThis = undefined;
|
||
return result;
|
||
}
|
||
|
||
function cancel() {
|
||
if (timerId !== undefined) {
|
||
clearTimeout(timerId);
|
||
}
|
||
lastInvokeTime = 0;
|
||
lastArgs = lastCallTime = lastThis = timerId = undefined;
|
||
}
|
||
|
||
function flush() {
|
||
return timerId === undefined ? result : trailingEdge(now());
|
||
}
|
||
|
||
function debounced() {
|
||
var time = now(),
|
||
isInvoking = shouldInvoke(time);
|
||
|
||
lastArgs = arguments;
|
||
lastThis = this;
|
||
lastCallTime = time;
|
||
|
||
if (isInvoking) {
|
||
if (timerId === undefined) {
|
||
return leadingEdge(lastCallTime);
|
||
}
|
||
if (maxing) {
|
||
// Handle invocations in a tight loop.
|
||
timerId = setTimeout(timerExpired, wait);
|
||
return invokeFunc(lastCallTime);
|
||
}
|
||
}
|
||
if (timerId === undefined) {
|
||
timerId = setTimeout(timerExpired, wait);
|
||
}
|
||
return result;
|
||
}
|
||
debounced.cancel = cancel;
|
||
debounced.flush = flush;
|
||
return debounced;
|
||
}
|
||
|
||
/**
|
||
* Creates a throttled function that only invokes `func` at most once per
|
||
* every `wait` milliseconds. The throttled function comes with a `cancel`
|
||
* method to cancel delayed `func` invocations and a `flush` method to
|
||
* immediately invoke them. Provide `options` to indicate whether `func`
|
||
* should be invoked on the leading and/or trailing edge of the `wait`
|
||
* timeout. The `func` is invoked with the last arguments provided to the
|
||
* throttled function. Subsequent calls to the throttled function return the
|
||
* result of the last `func` invocation.
|
||
*
|
||
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||
* invoked on the trailing edge of the timeout only if the throttled function
|
||
* is invoked more than once during the `wait` timeout.
|
||
*
|
||
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||
*
|
||
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||
* for details over the differences between `_.throttle` and `_.debounce`.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Function
|
||
* @param {Function} func The function to throttle.
|
||
* @param {number} [wait=0] The number of milliseconds to throttle invocations to.
|
||
* @param {Object} [options={}] The options object.
|
||
* @param {boolean} [options.leading=true]
|
||
* Specify invoking on the leading edge of the timeout.
|
||
* @param {boolean} [options.trailing=true]
|
||
* Specify invoking on the trailing edge of the timeout.
|
||
* @returns {Function} Returns the new throttled function.
|
||
* @example
|
||
*
|
||
* // Avoid excessively updating the position while scrolling.
|
||
* jQuery(window).on('scroll', _.throttle(updatePosition, 100));
|
||
*
|
||
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
||
* var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
|
||
* jQuery(element).on('click', throttled);
|
||
*
|
||
* // Cancel the trailing throttled invocation.
|
||
* jQuery(window).on('popstate', throttled.cancel);
|
||
*/
|
||
function throttle(func, wait, options) {
|
||
var leading = true,
|
||
trailing = true;
|
||
|
||
if (typeof func != 'function') {
|
||
throw new TypeError(FUNC_ERROR_TEXT);
|
||
}
|
||
if (isObject(options)) {
|
||
leading = 'leading' in options ? !!options.leading : leading;
|
||
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
||
}
|
||
return debounce(func, wait, {
|
||
'leading': leading,
|
||
'maxWait': wait,
|
||
'trailing': trailing
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Checks if `value` is the
|
||
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
|
||
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
|
||
* @example
|
||
*
|
||
* _.isObject({});
|
||
* // => true
|
||
*
|
||
* _.isObject([1, 2, 3]);
|
||
* // => true
|
||
*
|
||
* _.isObject(_.noop);
|
||
* // => true
|
||
*
|
||
* _.isObject(null);
|
||
* // => false
|
||
*/
|
||
function isObject(value) {
|
||
var type = typeof value;
|
||
return !!value && (type == 'object' || type == 'function');
|
||
}
|
||
|
||
/**
|
||
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
||
* and has a `typeof` result of "object".
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
|
||
* @example
|
||
*
|
||
* _.isObjectLike({});
|
||
* // => true
|
||
*
|
||
* _.isObjectLike([1, 2, 3]);
|
||
* // => true
|
||
*
|
||
* _.isObjectLike(_.noop);
|
||
* // => false
|
||
*
|
||
* _.isObjectLike(null);
|
||
* // => false
|
||
*/
|
||
function isObjectLike(value) {
|
||
return !!value && typeof value == 'object';
|
||
}
|
||
|
||
/**
|
||
* Checks if `value` is classified as a `Symbol` primitive or object.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
|
||
* @example
|
||
*
|
||
* _.isSymbol(Symbol.iterator);
|
||
* // => true
|
||
*
|
||
* _.isSymbol('abc');
|
||
* // => false
|
||
*/
|
||
function isSymbol(value) {
|
||
return typeof value == 'symbol' ||
|
||
(isObjectLike(value) && objectToString.call(value) == symbolTag);
|
||
}
|
||
|
||
/**
|
||
* Converts `value` to a number.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to process.
|
||
* @returns {number} Returns the number.
|
||
* @example
|
||
*
|
||
* _.toNumber(3.2);
|
||
* // => 3.2
|
||
*
|
||
* _.toNumber(Number.MIN_VALUE);
|
||
* // => 5e-324
|
||
*
|
||
* _.toNumber(Infinity);
|
||
* // => Infinity
|
||
*
|
||
* _.toNumber('3.2');
|
||
* // => 3.2
|
||
*/
|
||
function toNumber(value) {
|
||
if (typeof value == 'number') {
|
||
return value;
|
||
}
|
||
if (isSymbol(value)) {
|
||
return NAN;
|
||
}
|
||
if (isObject(value)) {
|
||
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
|
||
value = isObject(other) ? (other + '') : other;
|
||
}
|
||
if (typeof value != 'string') {
|
||
return value === 0 ? value : +value;
|
||
}
|
||
value = value.replace(reTrim, '');
|
||
var isBinary = reIsBinary.test(value);
|
||
return (isBinary || reIsOctal.test(value))
|
||
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
|
||
: (reIsBadHex.test(value) ? NAN : +value);
|
||
}
|
||
|
||
module.exports = throttle;
|
||
|
||
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||
},{}],55:[function(require,module,exports){
|
||
var wildcard = require('wildcard');
|
||
var reMimePartSplit = /[\/\+\.]/;
|
||
|
||
/**
|
||
# mime-match
|
||
|
||
A simple function to checker whether a target mime type matches a mime-type
|
||
pattern (e.g. image/jpeg matches image/jpeg OR image/*).
|
||
|
||
## Example Usage
|
||
|
||
<<< example.js
|
||
|
||
**/
|
||
module.exports = function(target, pattern) {
|
||
function test(pattern) {
|
||
var result = wildcard(pattern, target, reMimePartSplit);
|
||
|
||
// ensure that we have a valid mime type (should have two parts)
|
||
return result && result.length >= 2;
|
||
}
|
||
|
||
return pattern ? test(pattern.split(';')[0]) : test;
|
||
};
|
||
|
||
},{"wildcard":59}],56:[function(require,module,exports){
|
||
/**
|
||
* Create an event emitter with namespaces
|
||
* @name createNamespaceEmitter
|
||
* @example
|
||
* var emitter = require('./index')()
|
||
*
|
||
* emitter.on('*', function () {
|
||
* console.log('all events emitted', this.event)
|
||
* })
|
||
*
|
||
* emitter.on('example', function () {
|
||
* console.log('example event emitted')
|
||
* })
|
||
*/
|
||
module.exports = function createNamespaceEmitter () {
|
||
var emitter = {}
|
||
var _fns = emitter._fns = {}
|
||
|
||
/**
|
||
* Emit an event. Optionally namespace the event. Handlers are fired in the order in which they were added with exact matches taking precedence. Separate the namespace and event with a `:`
|
||
* @name emit
|
||
* @param {String} event – the name of the event, with optional namespace
|
||
* @param {...*} data – up to 6 arguments that are passed to the event listener
|
||
* @example
|
||
* emitter.emit('example')
|
||
* emitter.emit('demo:test')
|
||
* emitter.emit('data', { example: true}, 'a string', 1)
|
||
*/
|
||
emitter.emit = function emit (event, arg1, arg2, arg3, arg4, arg5, arg6) {
|
||
var toEmit = getListeners(event)
|
||
|
||
if (toEmit.length) {
|
||
emitAll(event, toEmit, [arg1, arg2, arg3, arg4, arg5, arg6])
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Create en event listener.
|
||
* @name on
|
||
* @param {String} event
|
||
* @param {Function} fn
|
||
* @example
|
||
* emitter.on('example', function () {})
|
||
* emitter.on('demo', function () {})
|
||
*/
|
||
emitter.on = function on (event, fn) {
|
||
if (!_fns[event]) {
|
||
_fns[event] = []
|
||
}
|
||
|
||
_fns[event].push(fn)
|
||
}
|
||
|
||
/**
|
||
* Create en event listener that fires once.
|
||
* @name once
|
||
* @param {String} event
|
||
* @param {Function} fn
|
||
* @example
|
||
* emitter.once('example', function () {})
|
||
* emitter.once('demo', function () {})
|
||
*/
|
||
emitter.once = function once (event, fn) {
|
||
function one () {
|
||
fn.apply(this, arguments)
|
||
emitter.off(event, one)
|
||
}
|
||
this.on(event, one)
|
||
}
|
||
|
||
/**
|
||
* Stop listening to an event. Stop all listeners on an event by only passing the event name. Stop a single listener by passing that event handler as a callback.
|
||
* You must be explicit about what will be unsubscribed: `emitter.off('demo')` will unsubscribe an `emitter.on('demo')` listener,
|
||
* `emitter.off('demo:example')` will unsubscribe an `emitter.on('demo:example')` listener
|
||
* @name off
|
||
* @param {String} event
|
||
* @param {Function} [fn] – the specific handler
|
||
* @example
|
||
* emitter.off('example')
|
||
* emitter.off('demo', function () {})
|
||
*/
|
||
emitter.off = function off (event, fn) {
|
||
var keep = []
|
||
|
||
if (event && fn) {
|
||
var fns = this._fns[event]
|
||
var i = 0
|
||
var l = fns ? fns.length : 0
|
||
|
||
for (i; i < l; i++) {
|
||
if (fns[i] !== fn) {
|
||
keep.push(fns[i])
|
||
}
|
||
}
|
||
}
|
||
|
||
keep.length ? this._fns[event] = keep : delete this._fns[event]
|
||
}
|
||
|
||
function getListeners (e) {
|
||
var out = _fns[e] ? _fns[e] : []
|
||
var idx = e.indexOf(':')
|
||
var args = (idx === -1) ? [e] : [e.substring(0, idx), e.substring(idx + 1)]
|
||
|
||
var keys = Object.keys(_fns)
|
||
var i = 0
|
||
var l = keys.length
|
||
|
||
for (i; i < l; i++) {
|
||
var key = keys[i]
|
||
if (key === '*') {
|
||
out = out.concat(_fns[key])
|
||
}
|
||
|
||
if (args.length === 2 && args[0] === key) {
|
||
out = out.concat(_fns[key])
|
||
break
|
||
}
|
||
}
|
||
|
||
return out
|
||
}
|
||
|
||
function emitAll (e, fns, args) {
|
||
var i = 0
|
||
var l = fns.length
|
||
|
||
for (i; i < l; i++) {
|
||
if (!fns[i]) break
|
||
fns[i].event = e
|
||
fns[i].apply(fns[i], args)
|
||
}
|
||
}
|
||
|
||
return emitter
|
||
}
|
||
|
||
},{}],57:[function(require,module,exports){
|
||
// This alphabet uses `A-Za-z0-9_-` symbols. The genetic algorithm helped
|
||
// optimize the gzip compression for this alphabet.
|
||
let urlAlphabet =
|
||
'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW'
|
||
|
||
let customAlphabet = (alphabet, size) => {
|
||
return () => {
|
||
let id = ''
|
||
// A compact alternative for `for (var i = 0; i < step; i++)`.
|
||
let i = size
|
||
while (i--) {
|
||
// `| 0` is more compact and faster than `Math.floor()`.
|
||
id += alphabet[(Math.random() * alphabet.length) | 0]
|
||
}
|
||
return id
|
||
}
|
||
}
|
||
|
||
let nanoid = (size = 21) => {
|
||
let id = ''
|
||
// A compact alternative for `for (var i = 0; i < step; i++)`.
|
||
let i = size
|
||
while (i--) {
|
||
// `| 0` is more compact and faster than `Math.floor()`.
|
||
id += urlAlphabet[(Math.random() * 64) | 0]
|
||
}
|
||
return id
|
||
}
|
||
|
||
module.exports = { nanoid, customAlphabet }
|
||
|
||
},{}],58:[function(require,module,exports){
|
||
var n,l,u,t,i,o,r,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function p(n){var l=n.parentNode;l&&l.removeChild(n)}function v(l,u,t){var i,o,r,f={};for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return h(l,f,i,o,null)}function h(n,t,i,o,r){var f={type:n,props:t,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null!=l.vnode&&l.vnode(f),f}function y(n){return n.children}function d(n,l){this.props=n,this.context=l}function _(n,l){if(null==l)return n.__?_(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?_(n):null}function k(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return k(n)}}function x(n){(!n.__d&&(n.__d=!0)&&i.push(n)&&!b.__r++||r!==l.debounceRendering)&&((r=l.debounceRendering)||o)(b)}function b(){for(var n;b.__r=i.length;)n=i.sort(function(n,l){return n.__v.__b-l.__v.__b}),i=[],n.some(function(n){var l,u,t,i,o,r;n.__d&&(o=(i=(l=n).__v).__e,(r=l.__P)&&(u=[],(t=a({},i)).__v=i.__v+1,I(r,i,t,l.__n,void 0!==r.ownerSVGElement,null!=i.__h?[o]:null,u,null==o?_(i):o,i.__h),T(u,i),i.__e!=o&&k(i)))})}function m(n,l,u,t,i,o,r,f,s,a){var p,v,d,k,x,b,m,A=t&&t.__k||c,P=A.length;for(u.__k=[],p=0;p<l.length;p++)if(null!=(k=u.__k[p]=null==(k=l[p])||"boolean"==typeof k?null:"string"==typeof k||"number"==typeof k||"bigint"==typeof k?h(null,k,null,null,k):Array.isArray(k)?h(y,{children:k},null,null,null):k.__b>0?h(k.type,k.props,k.key,null,k.__v):k)){if(k.__=u,k.__b=u.__b+1,null===(d=A[p])||d&&k.key==d.key&&k.type===d.type)A[p]=void 0;else for(v=0;v<P;v++){if((d=A[v])&&k.key==d.key&&k.type===d.type){A[v]=void 0;break}d=null}I(n,k,d=d||e,i,o,r,f,s,a),x=k.__e,(v=k.ref)&&d.ref!=v&&(m||(m=[]),d.ref&&m.push(d.ref,null,k),m.push(v,k.__c||x,k)),null!=x?(null==b&&(b=x),"function"==typeof k.type&&null!=k.__k&&k.__k===d.__k?k.__d=s=g(k,s,n):s=w(n,k,d,A,x,s),a||"option"!==u.type?"function"==typeof u.type&&(u.__d=s):n.value=""):s&&d.__e==s&&s.parentNode!=n&&(s=_(d))}for(u.__e=b,p=P;p--;)null!=A[p]&&("function"==typeof u.type&&null!=A[p].__e&&A[p].__e==u.__d&&(u.__d=_(t,p+1)),L(A[p],A[p]));if(m)for(p=0;p<m.length;p++)z(m[p],m[++p],m[++p])}function g(n,l,u){var t,i;for(t=0;t<n.__k.length;t++)(i=n.__k[t])&&(i.__=n,l="function"==typeof i.type?g(i,l,u):w(u,i,i,n.__k,i.__e,l));return l}function w(n,l,u,t,i,o){var r,f,e;if(void 0!==l.__d)r=l.__d,l.__d=void 0;else if(null==u||i!=o||null==i.parentNode)n:if(null==o||o.parentNode!==n)n.appendChild(i),r=null;else{for(f=o,e=0;(f=f.nextSibling)&&e<t.length;e+=2)if(f==i)break n;n.insertBefore(i,o),r=o}return void 0!==r?r:i.nextSibling}function A(n,l,u,t,i){var o;for(o in u)"children"===o||"key"===o||o in l||C(n,o,null,u[o],t);for(o in l)i&&"function"!=typeof l[o]||"children"===o||"key"===o||"value"===o||"checked"===o||u[o]===l[o]||C(n,o,l[o],u[o],t)}function P(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]=null==u?"":"number"!=typeof u||s.test(l)?u:u+"px"}function C(n,l,u,t,i){var o;n:if("style"===l)if("string"==typeof u)n.style.cssText=u;else{if("string"==typeof t&&(n.style.cssText=t=""),t)for(l in t)u&&l in u||P(n.style,l,"");if(u)for(l in u)t&&u[l]===t[l]||P(n.style,l,u[l])}else if("o"===l[0]&&"n"===l[1])o=l!==(l=l.replace(/Capture$/,"")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=u,u?t||n.addEventListener(l,o?H:$,o):n.removeEventListener(l,o?H:$,o);else if("dangerouslySetInnerHTML"!==l){if(i)l=l.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==l&&"list"!==l&&"form"!==l&&"tabIndex"!==l&&"download"!==l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null!=u&&(!1!==u||"a"===l[0]&&"r"===l[1])?n.setAttribute(l,u):n.removeAttribute(l))}}function $(n){this.l[n.type+!1](l.event?l.event(n):n)}function H(n){this.l[n.type+!0](l.event?l.event(n):n)}function I(n,u,t,i,o,r,f,e,c){var s,p,v,h,_,k,x,b,g,w,A,P=u.type;if(void 0!==u.constructor)return null;null!=t.__h&&(c=t.__h,e=u.__e=t.__e,u.__h=null,r=[e]),(s=l.__b)&&s(u);try{n:if("function"==typeof P){if(b=u.props,g=(s=P.contextType)&&i[s.__c],w=s?g?g.props.value:s.__:i,t.__c?x=(p=u.__c=t.__c).__=p.__E:("prototype"in P&&P.prototype.render?u.__c=p=new P(b,w):(u.__c=p=new d(b,w),p.constructor=P,p.render=M),g&&g.sub(p),p.props=b,p.state||(p.state={}),p.context=w,p.__n=i,v=p.__d=!0,p.__h=[]),null==p.__s&&(p.__s=p.state),null!=P.getDerivedStateFromProps&&(p.__s==p.state&&(p.__s=a({},p.__s)),a(p.__s,P.getDerivedStateFromProps(b,p.__s))),h=p.props,_=p.state,v)null==P.getDerivedStateFromProps&&null!=p.componentWillMount&&p.componentWillMount(),null!=p.componentDidMount&&p.__h.push(p.componentDidMount);else{if(null==P.getDerivedStateFromProps&&b!==h&&null!=p.componentWillReceiveProps&&p.componentWillReceiveProps(b,w),!p.__e&&null!=p.shouldComponentUpdate&&!1===p.shouldComponentUpdate(b,p.__s,w)||u.__v===t.__v){p.props=b,p.state=p.__s,u.__v!==t.__v&&(p.__d=!1),p.__v=u,u.__e=t.__e,u.__k=t.__k,u.__k.forEach(function(n){n&&(n.__=u)}),p.__h.length&&f.push(p);break n}null!=p.componentWillUpdate&&p.componentWillUpdate(b,p.__s,w),null!=p.componentDidUpdate&&p.__h.push(function(){p.componentDidUpdate(h,_,k)})}p.context=w,p.props=b,p.state=p.__s,(s=l.__r)&&s(u),p.__d=!1,p.__v=u,p.__P=n,s=p.render(p.props,p.state,p.context),p.state=p.__s,null!=p.getChildContext&&(i=a(a({},i),p.getChildContext())),v||null==p.getSnapshotBeforeUpdate||(k=p.getSnapshotBeforeUpdate(h,_)),A=null!=s&&s.type===y&&null==s.key?s.props.children:s,m(n,Array.isArray(A)?A:[A],u,t,i,o,r,f,e,c),p.base=u.__e,u.__h=null,p.__h.length&&f.push(p),x&&(p.__E=p.__=null),p.__e=!1}else null==r&&u.__v===t.__v?(u.__k=t.__k,u.__e=t.__e):u.__e=j(t.__e,u,t,i,o,r,f,c);(s=l.diffed)&&s(u)}catch(n){u.__v=null,(c||null!=r)&&(u.__e=e,u.__h=!!c,r[r.indexOf(e)]=null),l.__e(n,u,t)}}function T(n,u){l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function j(l,u,t,i,o,r,f,c){var s,a,v,h=t.props,y=u.props,d=u.type,k=0;if("svg"===d&&(o=!0),null!=r)for(;k<r.length;k++)if((s=r[k])&&(s===l||(d?s.localName==d:3==s.nodeType))){l=s,r[k]=null;break}if(null==l){if(null===d)return document.createTextNode(y);l=o?document.createElementNS("http://www.w3.org/2000/svg",d):document.createElement(d,y.is&&y),r=null,c=!1}if(null===d)h===y||c&&l.data===y||(l.data=y);else{if(r=r&&n.call(l.childNodes),a=(h=t.props||e).dangerouslySetInnerHTML,v=y.dangerouslySetInnerHTML,!c){if(null!=r)for(h={},k=0;k<l.attributes.length;k++)h[l.attributes[k].name]=l.attributes[k].value;(v||a)&&(v&&(a&&v.__html==a.__html||v.__html===l.innerHTML)||(l.innerHTML=v&&v.__html||""))}if(A(l,y,h,o,c),v)u.__k=[];else if(k=u.props.children,m(l,Array.isArray(k)?k:[k],u,t,i,o&&"foreignObject"!==d,r,f,r?r[0]:t.__k&&_(t,0),c),null!=r)for(k=r.length;k--;)null!=r[k]&&p(r[k]);c||("value"in y&&void 0!==(k=y.value)&&(k!==l.value||"progress"===d&&!k)&&C(l,"value",k,h.value,!1),"checked"in y&&void 0!==(k=y.checked)&&k!==l.checked&&C(l,"checked",k,h.checked,!1))}return l}function z(n,u,t){try{"function"==typeof n?n(u):n.current=u}catch(n){l.__e(n,t)}}function L(n,u,t){var i,o;if(l.unmount&&l.unmount(n),(i=n.ref)&&(i.current&&i.current!==n.__e||z(i,null,u)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(n){l.__e(n,u)}i.base=i.__P=null}if(i=n.__k)for(o=0;o<i.length;o++)i[o]&&L(i[o],u,"function"!=typeof n.type);t||null==n.__e||p(n.__e),n.__e=n.__d=void 0}function M(n,l,u){return this.constructor(n,u)}function N(u,t,i){var o,r,f;l.__&&l.__(u,t),r=(o="function"==typeof i)?null:i&&i.__k||t.__k,f=[],I(t,u=(!o&&i||t).__k=v(y,null,[u]),r||e,e,void 0!==t.ownerSVGElement,!o&&i?[i]:r?null:t.firstChild?n.call(t.childNodes):null,f,!o&&i?i:r?r.__e:t.firstChild,o),T(f,u)}n=c.slice,l={__e:function(n,l){for(var u,t,i;l=l.__;)if((u=l.__c)&&!u.__)try{if((t=u.constructor)&&null!=t.getDerivedStateFromError&&(u.setState(t.getDerivedStateFromError(n)),i=u.__d),null!=u.componentDidCatch&&(u.componentDidCatch(n),i=u.__d),i)return u.__E=u}catch(l){n=l}throw n}},u=0,t=function(n){return null!=n&&void 0===n.constructor},d.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=a({},this.state),"function"==typeof n&&(n=n(a({},u),this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),x(this))},d.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),x(this))},d.prototype.render=y,i=[],o="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,b.__r=0,f=0,exports.render=N,exports.hydrate=function n(l,u){N(l,u,n)},exports.createElement=v,exports.h=v,exports.Fragment=y,exports.createRef=function(){return{current:null}},exports.isValidElement=t,exports.Component=d,exports.cloneElement=function(l,u,t){var i,o,r,f=a({},l.props);for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),h(l.type,f,i||l.key,o||l.ref,null)},exports.createContext=function(n,l){var u={__c:l="__cC"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,t;return this.getChildContext||(u=[],(t={})[l]=this,this.getChildContext=function(){return t},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(x)},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u},exports.toChildArray=function n(l,u){return u=u||[],null==l||"boolean"==typeof l||(Array.isArray(l)?l.some(function(l){n(l,u)}):u.push(l)),u},exports.options=l;
|
||
|
||
|
||
},{}],59:[function(require,module,exports){
|
||
/* jshint node: true */
|
||
'use strict';
|
||
|
||
/**
|
||
# wildcard
|
||
|
||
Very simple wildcard matching, which is designed to provide the same
|
||
functionality that is found in the
|
||
[eve](https://github.com/adobe-webplatform/eve) eventing library.
|
||
|
||
## Usage
|
||
|
||
It works with strings:
|
||
|
||
<<< examples/strings.js
|
||
|
||
Arrays:
|
||
|
||
<<< examples/arrays.js
|
||
|
||
Objects (matching against keys):
|
||
|
||
<<< examples/objects.js
|
||
|
||
While the library works in Node, if you are are looking for file-based
|
||
wildcard matching then you should have a look at:
|
||
|
||
<https://github.com/isaacs/node-glob>
|
||
**/
|
||
|
||
function WildcardMatcher(text, separator) {
|
||
this.text = text = text || '';
|
||
this.hasWild = ~text.indexOf('*');
|
||
this.separator = separator;
|
||
this.parts = text.split(separator);
|
||
}
|
||
|
||
WildcardMatcher.prototype.match = function(input) {
|
||
var matches = true;
|
||
var parts = this.parts;
|
||
var ii;
|
||
var partsCount = parts.length;
|
||
var testParts;
|
||
|
||
if (typeof input == 'string' || input instanceof String) {
|
||
if (!this.hasWild && this.text != input) {
|
||
matches = false;
|
||
} else {
|
||
testParts = (input || '').split(this.separator);
|
||
for (ii = 0; matches && ii < partsCount; ii++) {
|
||
if (parts[ii] === '*') {
|
||
continue;
|
||
} else if (ii < testParts.length) {
|
||
matches = parts[ii] === testParts[ii];
|
||
} else {
|
||
matches = false;
|
||
}
|
||
}
|
||
|
||
// If matches, then return the component parts
|
||
matches = matches && testParts;
|
||
}
|
||
}
|
||
else if (typeof input.splice == 'function') {
|
||
matches = [];
|
||
|
||
for (ii = input.length; ii--; ) {
|
||
if (this.match(input[ii])) {
|
||
matches[matches.length] = input[ii];
|
||
}
|
||
}
|
||
}
|
||
else if (typeof input == 'object') {
|
||
matches = {};
|
||
|
||
for (var key in input) {
|
||
if (this.match(key)) {
|
||
matches[key] = input[key];
|
||
}
|
||
}
|
||
}
|
||
|
||
return matches;
|
||
};
|
||
|
||
module.exports = function(text, test, separator) {
|
||
var matcher = new WildcardMatcher(text, separator || /[\/\.]/);
|
||
if (typeof test != 'undefined') {
|
||
return matcher.match(test);
|
||
}
|
||
|
||
return matcher;
|
||
};
|
||
|
||
},{}],60:[function(require,module,exports){
|
||
// We need a custom build of Uppy because we do not use webpack for
|
||
// our JS modules/build. The only way to get what you want from Uppy
|
||
// is to use the webpack modules or to include the entire Uppy project
|
||
// including all plugins in a single JS file. This way we can just
|
||
// use the plugins we actually want.
|
||
window.Uppy = {}
|
||
Uppy.Core = require('@uppy/core')
|
||
Uppy.XHRUpload = require('@uppy/xhr-upload')
|
||
Uppy.AwsS3 = require('@uppy/aws-s3')
|
||
Uppy.AwsS3Multipart = require('@uppy/aws-s3-multipart')
|
||
Uppy.DropTarget = require('@uppy/drop-target')
|
||
Uppy.Utils = {
|
||
delay: require('@uppy/utils/lib/delay'),
|
||
EventTracker: require('@uppy/utils/lib/EventTracker'),
|
||
AbortControllerLib: require('@uppy/utils/lib/AbortController')
|
||
}
|
||
|
||
},{"@uppy/aws-s3":5,"@uppy/aws-s3-multipart":3,"@uppy/core":20,"@uppy/drop-target":24,"@uppy/utils/lib/AbortController":26,"@uppy/utils/lib/EventTracker":27,"@uppy/utils/lib/delay":32,"@uppy/xhr-upload":52}]},{},[60]);
|