discourse/vendor/assets/javascripts/uppy.js

7825 lines
238 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(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){
// 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('./packages/@uppy/core/lib/index.js')
Uppy.BasePlugin = Uppy.Core.BasePlugin
Uppy.XHRUpload = require('./packages/@uppy/xhr-upload/lib/index.js')
Uppy.AwsS3 = require('./packages/@uppy/aws-s3/lib/index.js')
Uppy.AwsS3Multipart = require('./packages/@uppy/aws-s3-multipart/lib/index.js')
Uppy.DropTarget = require('./packages/@uppy/drop-target/lib/index.js')
},{"./packages/@uppy/aws-s3-multipart/lib/index.js":14,"./packages/@uppy/aws-s3/lib/index.js":34,"./packages/@uppy/core/lib/index.js":60,"./packages/@uppy/drop-target/lib/index.js":75,"./packages/@uppy/xhr-upload/lib/index.js":83}],2:[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
}
}
},{}],3:[function(require,module,exports){
/**
* cuid.js
* Collision-resistant UID generator for browsers and node.
* Sequential for fast db lookups and recency sorting.
* Safe for element IDs and server-side lookups.
*
* Extracted from CLCTR
*
* Copyright (c) Eric Elliott 2012
* MIT License
*/
var fingerprint = require('./lib/fingerprint.js');
var pad = require('./lib/pad.js');
var getRandomValue = require('./lib/getRandomValue.js');
var c = 0,
blockSize = 4,
base = 36,
discreteValues = Math.pow(base, blockSize);
function randomBlock () {
return pad((getRandomValue() *
discreteValues << 0)
.toString(base), blockSize);
}
function safeCounter () {
c = c < discreteValues ? c : 0;
c++; // this is not subliminal
return c - 1;
}
function cuid () {
// Starting with a lowercase letter makes
// it HTML element ID friendly.
var letter = 'c', // hard-coded allows for sequential access
// timestamp
// warning: this exposes the exact date and time
// that the uid was created.
timestamp = (new Date().getTime()).toString(base),
// Prevent same-machine collisions.
counter = pad(safeCounter().toString(base), blockSize),
// A few chars to generate distinct ids for different
// clients (so different computers are far less
// likely to generate the same id)
print = fingerprint(),
// Grab some more chars from Math.random()
random = randomBlock() + randomBlock();
return letter + timestamp + counter + print + random;
}
cuid.slug = function slug () {
var date = new Date().getTime().toString(36),
counter = safeCounter().toString(36).slice(-4),
print = fingerprint().slice(0, 1) +
fingerprint().slice(-1),
random = randomBlock().slice(-2);
return date.slice(-2) +
counter + print + random;
};
cuid.isCuid = function isCuid (stringToCheck) {
if (typeof stringToCheck !== 'string') return false;
if (stringToCheck.startsWith('c')) return true;
return false;
};
cuid.isSlug = function isSlug (stringToCheck) {
if (typeof stringToCheck !== 'string') return false;
var stringLength = stringToCheck.length;
if (stringLength >= 7 && stringLength <= 10) return true;
return false;
};
cuid.fingerprint = fingerprint;
module.exports = cuid;
},{"./lib/fingerprint.js":4,"./lib/getRandomValue.js":5,"./lib/pad.js":6}],4:[function(require,module,exports){
var pad = require('./pad.js');
var env = typeof window === 'object' ? window : self;
var globalCount = Object.keys(env).length;
var mimeTypesLength = navigator.mimeTypes ? navigator.mimeTypes.length : 0;
var clientId = pad((mimeTypesLength +
navigator.userAgent.length).toString(36) +
globalCount.toString(36), 4);
module.exports = function fingerprint () {
return clientId;
};
},{"./pad.js":6}],5:[function(require,module,exports){
var getRandomValue;
var crypto = typeof window !== 'undefined' &&
(window.crypto || window.msCrypto) ||
typeof self !== 'undefined' &&
self.crypto;
if (crypto) {
var lim = Math.pow(2, 32) - 1;
getRandomValue = function () {
return Math.abs(crypto.getRandomValues(new Uint32Array(1))[0] / lim);
};
} else {
getRandomValue = Math.random;
}
module.exports = getRandomValue;
},{}],6:[function(require,module,exports){
module.exports = function pad (num, size) {
var s = '000000000' + num;
return s.substr(s.length - size);
};
},{}],7:[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 : {})
},{}],8:[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":12}],9:[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
}
},{}],10:[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;
},{}],11:[function(require,module,exports){
var has = Object.prototype.hasOwnProperty
/**
* Stringify an object for use in a query string.
*
* @param {Object} obj - The object.
* @param {string} prefix - When nesting, the parent key.
* keys in `obj` will be stringified as `prefix[key]`.
* @returns {string}
*/
module.exports = function queryStringify (obj, prefix) {
var pairs = []
for (var key in obj) {
if (!has.call(obj, key)) {
continue
}
var value = obj[key]
var enkey = encodeURIComponent(key)
var pair
if (typeof value === 'object') {
pair = queryStringify(value, prefix ? prefix + '[' + enkey + ']' : enkey)
} else {
pair = (prefix ? prefix + '[' + enkey + ']' : enkey) + '=' + encodeURIComponent(value)
}
pairs.push(pair)
}
return pairs.join('&')
}
},{}],12:[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;
};
},{}],13:[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; }
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;
this.lockedCandidatesForBatch = [];
_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 = {}) {
const really = opts.really || false;
if (!really) return this.pause();
_classPrivateFieldLooseBase(this, _abortUpload)[_abortUpload]();
}
}
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++) {
if (this.lockedCandidatesForBatch.includes(i)) continue;
const state = this.chunkState[i];
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({
before,
attempt,
after
}) {
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) {
this.lockedCandidatesForBatch.push(...candidates);
const result = await this.options.prepareUploadParts({
key: this.key,
uploadId: this.uploadId,
partNumbers: candidates.map(index => index + 1)
});
const valid = typeof (result == null ? void 0 : result.presignedUrls) === 'object';
if (!valid) {
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) {
const body = this.chunks[index];
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, total) {
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).map(key => {
xhr.setRequestHeader(key, headers[key]);
});
}
xhr.responseType = 'text';
function cleanup() {
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', ev => {
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;
}
_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":23,"@uppy/utils/lib/delay":27}],14:[function(require,module,exports){
"use strict";
var _class, _temp;
const {
BasePlugin
} = require('@uppy/core');
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 Uploader = 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 (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).map(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, {
key,
uploadId
}) {
this.assertHost('listParts');
const filename = encodeURIComponent(key);
return this.client.get(`s3/multipart/${uploadId}?key=${filename}`).then(assertServerError);
}
prepareUploadParts(file, {
key,
uploadId,
partNumbers
}) {
this.assertHost('prepareUploadParts');
const filename = encodeURIComponent(key);
return this.client.get(`s3/multipart/${uploadId}/batch?key=${filename}?partNumbers=${partNumbers.join(',')}`).then(assertServerError);
}
completeMultipartUpload(file, {
key,
uploadId,
parts
}) {
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, {
key,
uploadId
}) {
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 Uploader(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, removed => {
queuedRequest.abort();
socket.send('pause', {});
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('pause', {});
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', filesToRetry => {
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 = require('../package.json').version, _temp);
},{"../package.json":32,"./MultipartUploader":13,"@uppy/companion-client":20,"@uppy/core":60,"@uppy/utils/lib/EventTracker":24,"@uppy/utils/lib/RateLimitedQueue":26,"@uppy/utils/lib/emitSocketProgress":28,"@uppy/utils/lib/getSocketHost":31}],15:[function(require,module,exports){
'use strict';
class AuthError extends Error {
constructor() {
super('Authorization required');
this.name = 'AuthError';
this.isAuthError = true;
}
}
module.exports = AuthError;
},{}],16:[function(require,module,exports){
'use strict';
const qsStringify = require('qs-stringify');
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(([headers, token]) => {
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);
}
authUrl(queries = {}) {
if (this.preAuthToken) {
queries.uppyPreAuthToken = this.preAuthToken;
}
let strigifiedQueries = qsStringify(queries);
strigifiedQueries = strigifiedQueries ? `?${strigifiedQueries}` : strigifiedQueries;
return `${this.hostname}/${this.id}/connect${strigifiedQueries}`;
}
fileUrl(id) {
return `${this.hostname}/${this.id}/get/${id}`;
}
fetchPreAuthToken() {
if (!this.companionKeysParams) {
return Promise.resolve();
}
return this.post(`${this.id}/preauth/`, {
params: this.companionKeysParams
}).then(res => {
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(([response]) => 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 {
// does not start with https://
if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) {
plugin.opts.companionAllowedHosts = `https://${opts.companionUrl.replace(/^\/\//, '')}`;
} else {
plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin;
}
}
plugin.storage = plugin.opts.storage || tokenStorage;
}
};
},{"./RequestClient":17,"./tokenStorage":21,"qs-stringify":11}],17:[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(([allowedHeaders, headers]) => {
// 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 = require('../package.json').version, _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);
};
}
},{"../package.json":22,"./AuthError":15,"@uppy/utils/lib/fetchWithNetworkError":29}],18:[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":17}],19:[function(require,module,exports){
"use strict";
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":9}],20:[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":16,"./RequestClient":17,"./SearchProvider":18,"./Socket":19}],21:[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();
});
};
},{}],22:[function(require,module,exports){
module.exports={
"name": "@uppy/companion-client",
"description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"uppy",
"uppy-plugin",
"companion",
"provider"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@uppy/utils": "file:../utils",
"namespace-emitter": "^2.0.1",
"qs-stringify": "^1.1.0"
}
}
},{}],23:[function(require,module,exports){
"use strict";
/**
* Little AbortController proxy module so we can swap out the implementation easily later.
*/
exports.AbortController = AbortController;
exports.AbortSignal = AbortSignal;
exports.createAbortError = (message = 'Aborted') => new DOMException(message, 'AbortError');
},{}],24:[function(require,module,exports){
"use strict";
/**
* Create a wrapper around an event emitter with a `remove` method to remove
* all events that were added using the wrapped emitter.
*/
module.exports = class EventTracker {
constructor(emitter) {
this._events = [];
this._emitter = emitter;
}
on(event, fn) {
this._events.push([event, fn]);
return this._emitter.on(event, fn);
}
remove() {
this._events.forEach(([event, fn]) => {
this._emitter.off(event, fn);
});
}
};
},{}],25:[function(require,module,exports){
"use strict";
class NetworkError extends Error {
constructor(error, xhr = null) {
super(`This looks like a network error, the endpoint might be blocked by an internet provider or a firewall.\n\nSource error: [${error}]`);
this.isNetworkError = true;
this.request = xhr;
}
}
module.exports = NetworkError;
},{}],26:[function(require,module,exports){
"use strict";
const findIndex = require('./findIndex');
function createCancelError() {
return new Error('Cancelled');
}
class RateLimitedQueue {
constructor(limit) {
if (typeof limit !== 'number' || limit === 0) {
this.limit = Infinity;
} else {
this.limit = limit;
}
this.activeRequests = 0;
this.queuedHandlers = [];
}
_call(fn) {
this.activeRequests += 1;
let done = false;
let cancelActive;
try {
cancelActive = fn();
} catch (err) {
this.activeRequests -= 1;
throw err;
}
return {
abort: () => {
if (done) return;
done = true;
this.activeRequests -= 1;
cancelActive();
this._queueNext();
},
done: () => {
if (done) return;
done = true;
this.activeRequests -= 1;
this._queueNext();
}
};
}
_queueNext() {
// 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)
Promise.resolve().then(() => {
this._next();
});
}
_next() {
if (this.activeRequests >= this.limit) {
return;
}
if (this.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 = this.queuedHandlers.shift();
const handler = this._call(next.fn);
next.abort = handler.abort;
next.done = handler.done;
}
_queue(fn, options = {}) {
const handler = {
fn,
priority: options.priority || 0,
abort: () => {
this._dequeue(handler);
},
done: () => {
throw new Error('Cannot mark a queued request as done: this indicates a bug');
}
};
const index = findIndex(this.queuedHandlers, other => {
return handler.priority > other.priority;
});
if (index === -1) {
this.queuedHandlers.push(handler);
} else {
this.queuedHandlers.splice(index, 0, handler);
}
return handler;
}
_dequeue(handler) {
const index = this.queuedHandlers.indexOf(handler);
if (index !== -1) {
this.queuedHandlers.splice(index, 1);
}
}
run(fn, queueOptions) {
if (this.activeRequests < this.limit) {
return this._call(fn);
}
return this._queue(fn, queueOptions);
}
wrapPromiseFunction(fn, queueOptions) {
return (...args) => {
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;
};
}
}
module.exports = {
RateLimitedQueue,
internalRateLimitedQueue: Symbol('__queue')
};
},{"./findIndex":30}],27:[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) => {
if (opts && opts.signal && opts.signal.aborted) {
return reject(createAbortError());
}
function onabort() {
clearTimeout(timeout);
cleanup();
reject(createAbortError());
}
const timeout = setTimeout(() => {
cleanup();
resolve();
}, ms);
if (opts && opts.signal) {
opts.signal.addEventListener('abort', onabort);
}
function cleanup() {
if (opts && opts.signal) {
opts.signal.removeEventListener('abort', onabort);
}
}
});
};
},{"./AbortController":23}],28:[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":7}],29:[function(require,module,exports){
"use strict";
const NetworkError = require('./NetworkError');
/**
* Wrapper around window.fetch that throws a NetworkError when appropriate
*/
module.exports = function fetchWithNetworkError(...options) {
return fetch(...options).catch(err => {
if (err.name === 'AbortError') {
throw err;
} else {
throw new NetworkError(err);
}
});
};
},{"./NetworkError":25}],30:[function(require,module,exports){
"use strict";
/**
* Array.prototype.findIndex ponyfill for old browsers.
*
* @param {Array} array
* @param {Function} predicate
* @returns {number}
*/
module.exports = function findIndex(array, predicate) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) return i;
}
return -1;
};
},{}],31:[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}`;
};
},{}],32:[function(require,module,exports){
module.exports={
"name": "@uppy/aws-s3-multipart",
"description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"aws s3",
"amazon s3",
"s3",
"uppy",
"uppy-plugin",
"multipart"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@uppy/companion-client": "file:../companion-client",
"@uppy/utils": "file:../utils"
},
"devDependencies": {
"whatwg-fetch": "3.6.2",
"nock": "^13.1.0"
},
"peerDependencies": {
"@uppy/core": "^1.0.0"
}
}
},{}],33:[function(require,module,exports){
"use strict";
const cuid = require('cuid');
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) {
// 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;
} // See XHRUpload
function setTypeInBlob(file) {
const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type);
return dataWithUpdatedType;
}
module.exports = class MiniXHRUpload {
constructor(uppy, opts) {
this.uppy = uppy;
this.opts = {
validateStatus(status, responseText, response) {
return status >= 200 && status < 300;
},
...opts
};
this.requests = opts[internalRateLimitedQueue];
this.uploaderEvents = Object.create(null);
this.i18n = opts.i18n;
}
_getOptions(file) {
const {
uppy
} = this;
const overrides = uppy.getState().xhrUpload;
const opts = { ...this.opts,
...(overrides || {}),
...(file.xhrUpload || {}),
headers: {}
};
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;
}
uploadFile(id, current, total) {
const file = this.uppy.getFile(id);
if (file.error) {
throw new Error(file.error);
} else if (file.isRemote) {
return this._uploadRemoteFile(file, current, total);
}
return this._uploadLocalFile(file, current, total);
}
_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]);
});
}
_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;
}
_createBareUpload(file, opts) {
return file.data;
}
_onFileRemoved(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', filesToRetry => {
if (!this.uppy.getFile(fileID)) return;
cb();
});
}
_onCancelAll(fileID, cb) {
this.uploaderEvents[fileID].on('cancel-all', () => {
if (!this.uppy.getFile(fileID)) return;
cb();
});
}
_uploadLocalFile(file, current, total) {
const opts = this._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 ? this._createFormDataUpload(file, opts) : this._createBareUpload(file, opts);
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 = cuid();
xhr.upload.addEventListener('loadstart', ev => {
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();
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', ev => {
this.uppy.log(`[AwsS3/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;
}
Object.keys(opts.headers).forEach(header => {
xhr.setRequestHeader(header, opts.headers[header]);
});
const queuedRequest = this.requests.run(() => {
xhr.send(data);
return () => {
timer.done();
xhr.abort();
};
}, {
priority: 1
});
this._onFileRemoved(file.id, () => {
queuedRequest.abort();
reject(new Error('File removed'));
});
this._onCancelAll(file.id, () => {
queuedRequest.abort();
reject(new Error('Upload cancelled'));
});
});
}
_uploadRemoteFile(file, current, total) {
const opts = this._getOptions(file);
return new Promise((resolve, reject) => {
// This is done in index.js in the S3 plugin.
// 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._onFileRemoved(file.id, () => {
socket.send('pause', {});
queuedRequest.abort();
resolve(`upload ${file.id} was removed`);
});
this._onCancelAll(file.id, () => {
socket.send('pause', {});
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,
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);
});
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);
});
});
}
};
},{"@uppy/companion-client":41,"@uppy/utils/lib/EventTracker":44,"@uppy/utils/lib/NetworkError":45,"@uppy/utils/lib/ProgressTimeout":46,"@uppy/utils/lib/RateLimitedQueue":47,"@uppy/utils/lib/emitSocketProgress":49,"@uppy/utils/lib/getSocketHost":52,"@uppy/utils/lib/isNetworkError":54,"cuid":3}],34:[function(require,module,exports){
"use strict";
var _class, _temp;
/**
* 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');
const Translator = require('@uppy/utils/lib/Translator');
const {
RateLimitedQueue,
internalRateLimitedQueue
} = require('@uppy/utils/lib/RateLimitedQueue');
const settle = require('@uppy/utils/lib/settle');
const hasProperty = require('@uppy/utils/lib/hasProperty');
const {
RequestClient
} = require('@uppy/companion-client');
const qsStringify = require('qs-stringify');
const MiniXHRUpload = require('./MiniXHRUpload');
const isXml = require('./isXml');
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;
} // warning deduplication flag: see `getResponseData()` XHRUpload option definition
let warnedSuccessActionStatus = false;
module.exports = (_temp = _class = class AwsS3 extends BasePlugin {
constructor(uppy, opts) {
super(uppy, opts);
this.type = 'uploader';
this.id = this.opts.id || 'AwsS3';
this.title = 'AWS S3';
this.defaultLocale = {
strings: {
timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
}
};
const defaultOptions = {
timeout: 30 * 1000,
limit: 0,
metaFields: [],
// have to opt in
getUploadParameters: this.getUploadParameters.bind(this)
};
this.opts = { ...defaultOptions,
...opts
};
this.client = new RequestClient(uppy, opts);
this.handleUpload = this.handleUpload.bind(this);
this.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 = {};
this.opts.metaFields.forEach(key => {
if (file.meta[key] != null) {
metadata[key] = file.meta[key].toString();
}
});
const query = qsStringify({
filename,
type,
metadata
});
return this.client.get(`s3/params?${query}`).then(assertServerError);
}
validateParameters(file, params) {
const valid = typeof params === 'object' && params && 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.`);
console.error(err);
throw err;
}
const methodIsValid = params.method == null || /^(put|post)$/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.`);
console.error(err);
throw err;
}
}
handleUpload(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) {
const {
id
} = file;
if (hasProperty(paramsPromises, id)) {
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 = this.requests.wrapPromiseFunction(file => {
return this.opts.getUploadParameters(file);
});
const numberOfFiles = fileIDs.length;
return settle(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);
this.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 this._uploader.uploadFile(file.id, index, numberOfFiles);
}).catch(error => {
delete paramsPromises[id];
const file = this.uppy.getFile(id);
this.uppy.emit('upload-error', file, error);
});
})).then(settled => {
// cleanup.
this.uppy.off('file-removed', onremove);
return settled;
});
}
install() {
const {
uppy
} = this;
this.uppy.addUploader(this.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')
};
} // 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;
}
const error = getXmlValue(content, 'Message');
return new Error(error);
}
const xhrOptions = {
fieldName: 'file',
responseUrlFieldName: 'location',
timeout: this.opts.timeout,
// Share the rate limiting queue with XHRUpload.
[internalRateLimitedQueue]: this.requests,
responseType: 'text',
getResponseData: this.opts.getResponseData || defaultGetResponseData,
getResponseError: defaultGetResponseError
}; // Only for MiniXHRUpload, remove once we can depend on XHRUpload directly again
xhrOptions.i18n = this.i18n; // Revert to `this.uppy.use(XHRUpload)` once the big comment block at the top of
// this file is solved
this._uploader = new MiniXHRUpload(this.uppy, xhrOptions);
}
uninstall() {
this.uppy.removeUploader(this.handleUpload);
}
}, _class.VERSION = require('../package.json').version, _temp);
},{"../package.json":56,"./MiniXHRUpload":33,"./isXml":35,"@uppy/companion-client":41,"@uppy/core":60,"@uppy/utils/lib/RateLimitedQueue":47,"@uppy/utils/lib/Translator":48,"@uppy/utils/lib/hasProperty":53,"@uppy/utils/lib/settle":55,"qs-stringify":11}],35:[function(require,module,exports){
"use strict";
/**
* 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;
},{}],36:[function(require,module,exports){
arguments[4][15][0].apply(exports,arguments)
},{"dup":15}],37:[function(require,module,exports){
arguments[4][16][0].apply(exports,arguments)
},{"./RequestClient":38,"./tokenStorage":42,"dup":16,"qs-stringify":11}],38:[function(require,module,exports){
arguments[4][17][0].apply(exports,arguments)
},{"../package.json":43,"./AuthError":36,"@uppy/utils/lib/fetchWithNetworkError":50,"dup":17}],39:[function(require,module,exports){
arguments[4][18][0].apply(exports,arguments)
},{"./RequestClient":38,"dup":18}],40:[function(require,module,exports){
arguments[4][19][0].apply(exports,arguments)
},{"dup":19,"namespace-emitter":9}],41:[function(require,module,exports){
arguments[4][20][0].apply(exports,arguments)
},{"./Provider":37,"./RequestClient":38,"./SearchProvider":39,"./Socket":40,"dup":20}],42:[function(require,module,exports){
arguments[4][21][0].apply(exports,arguments)
},{"dup":21}],43:[function(require,module,exports){
arguments[4][22][0].apply(exports,arguments)
},{"dup":22}],44:[function(require,module,exports){
arguments[4][24][0].apply(exports,arguments)
},{"dup":24}],45:[function(require,module,exports){
arguments[4][25][0].apply(exports,arguments)
},{"dup":25}],46:[function(require,module,exports){
"use strict";
/**
* 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) {
this._timeout = timeout;
this._onTimedOut = timeoutHandler;
this._isDone = false;
this._aliveTimer = null;
this._onTimedOut = this._onTimedOut.bind(this);
}
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 (this._isDone) return;
if (this._timeout > 0) {
if (this._aliveTimer) clearTimeout(this._aliveTimer);
this._aliveTimer = setTimeout(this._onTimedOut, this._timeout);
}
}
done() {
if (this._aliveTimer) {
clearTimeout(this._aliveTimer);
this._aliveTimer = null;
}
this._isDone = true;
}
}
module.exports = ProgressTimeout;
},{}],47:[function(require,module,exports){
arguments[4][26][0].apply(exports,arguments)
},{"./findIndex":51,"dup":26}],48:[function(require,module,exports){
"use strict";
const has = require('./hasProperty');
/**
* 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 = class Translator {
/**
* @param {object|Array<object>} locales - locale or list of locales.
*/
constructor(locales) {
this.locale = {
strings: {},
pluralize(n) {
if (n === 1) {
return 0;
}
return 1;
}
};
if (Array.isArray(locales)) {
locales.forEach(locale => this._apply(locale));
} else {
this._apply(locales);
}
}
_apply(locale) {
if (!locale || !locale.strings) {
return;
}
const prevLocale = this.locale;
this.locale = { ...prevLocale,
strings: { ...prevLocale.strings,
...locale.strings
}
};
this.locale.pluralize = locale.pluralize || prevLocale.pluralize;
}
/**
* 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
*/
interpolate(phrase, options) {
const dollarRegex = /\$/g;
const dollarBillsYall = '$$$$';
let interpolated = [phrase];
for (const arg in options) {
if (arg !== '_' && has(options, 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;
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 wed get this: [object Object] hello [object Object] my <button>
if (typeof chunk !== 'string') {
return newParts.push(chunk);
}
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;
}
}
/**
* 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 this.interpolate(string[plural], options);
}
throw new Error('Attempted to use a string with plural forms, but no value was given for %{smart_count}');
}
return this.interpolate(string, options);
}
};
},{"./hasProperty":53}],49:[function(require,module,exports){
arguments[4][28][0].apply(exports,arguments)
},{"dup":28,"lodash.throttle":7}],50:[function(require,module,exports){
arguments[4][29][0].apply(exports,arguments)
},{"./NetworkError":45,"dup":29}],51:[function(require,module,exports){
arguments[4][30][0].apply(exports,arguments)
},{"dup":30}],52:[function(require,module,exports){
arguments[4][31][0].apply(exports,arguments)
},{"dup":31}],53:[function(require,module,exports){
"use strict";
module.exports = function has(object, key) {
return Object.prototype.hasOwnProperty.call(object, key);
};
},{}],54:[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;
},{}],55:[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
};
});
};
},{}],56:[function(require,module,exports){
module.exports={
"name": "@uppy/aws-s3",
"description": "Upload to Amazon S3 with Uppy",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"aws s3",
"amazon s3",
"s3",
"uppy",
"uppy-plugin"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@uppy/companion-client": "file:../companion-client",
"@uppy/utils": "file:../utils",
"@uppy/xhr-upload": "file:../xhr-upload",
"cuid": "^2.1.1",
"qs-stringify": "^1.1.0"
},
"devDependencies": {
"whatwg-fetch": "3.6.2"
},
"peerDependencies": {
"@uppy/core": "^1.0.0"
}
}
},{}],57:[function(require,module,exports){
"use strict";
/**
* 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 = {}) {
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
onMount() {} // 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":65}],58:[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; }
const {
render,
h,
createRef,
cloneElement
} = 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 (...args) => {
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(...args) {
super(...args);
Object.defineProperty(this, _updateUI, {
writable: true,
value: void 0
});
}
/**
* Check if supplied `target` is a DOM element or an `object`.
* If its 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; // 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), targetElement);
this.afterUpdate();
});
this.uppy.log(`Installing ${callerPluginName} to a DOM element '${target}'`);
if (this.opts.replaceTargetContent) {
// Although you could remove the child nodes using DOM APIs (container.innerHTML = ''),
// this isn't recommended because the component might need to do additional cleanup when it is removed.
// To remove the rendered content and run any cleanup processes, render an empty element into the container:
render(h(null), targetElement);
}
render(this.render(this.uppy.getState()), targetElement);
this.el = targetElement.firstElementChild;
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();
}
}
}
module.exports = UIPlugin;
},{"./BasePlugin":57,"@uppy/utils/lib/findDOMElement":66,"preact":10}],59:[function(require,module,exports){
"use strict";
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';
};
},{}],60:[function(require,module,exports){
"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; }
/* global AggregateError */
const Translator = require('@uppy/utils/lib/Translator');
const ee = require('namespace-emitter');
const cuid = require('cuid');
const throttle = require('lodash.throttle');
const prettierBytes = require('@transloadit/prettier-bytes');
const match = require('mime-match');
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 UIPlugin = require('./UIPlugin');
const BasePlugin = require('./BasePlugin'); // Exported from here.
class RestrictionError extends Error {
constructor(...args) {
super(...args);
this.isRestriction = true;
}
}
if (typeof AggregateError === 'undefined') {
// eslint-disable-next-line no-global-assign
globalThis.AggregateError = class AggregateError extends Error {
constructor(message, errors) {
super(message);
this.errors = errors;
}
};
}
class AggregateRestrictionError extends AggregateError {
constructor(...args) {
super(...args);
this.isRestriction = true;
}
}
/**
* Uppy Core module.
* Manages plugins, state updates, acts as an event bus,
* adds/removes files and metadata.
*/
var _plugins = /*#__PURE__*/_classPrivateFieldLooseKey("plugins");
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 _checkRestrictions = /*#__PURE__*/_classPrivateFieldLooseKey("checkRestrictions");
var _checkMinNumberOfFiles = /*#__PURE__*/_classPrivateFieldLooseKey("checkMinNumberOfFiles");
var _checkRequiredMetaFields = /*#__PURE__*/_classPrivateFieldLooseKey("checkRequiredMetaFields");
var _showOrLogErrorAndThrow = /*#__PURE__*/_classPrivateFieldLooseKey("showOrLogErrorAndThrow");
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, _showOrLogErrorAndThrow, {
value: _showOrLogErrorAndThrow2
});
Object.defineProperty(this, _checkRequiredMetaFields, {
value: _checkRequiredMetaFields2
});
Object.defineProperty(this, _checkMinNumberOfFiles, {
value: _checkMinNumberOfFiles2
});
Object.defineProperty(this, _checkRestrictions, {
value: _checkRestrictions2
});
Object.defineProperty(this, _plugins, {
writable: true,
value: Object.create(null)
});
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 = {
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}',
noNewAlreadyUploading: 'Cannot add new files: already uploading',
noDuplicates: 'Cannot add the duplicate file \'%{fileName}\', it already exists',
companionError: 'Connection with Companion failed',
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}',
searchImages: 'Search for images',
enterTextToSearch: 'Enter text to search for images',
backToSearch: 'Back to 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}'
}
}
};
const defaultOptions = {
id: 'uppy',
autoProceed: false,
allowMultipleUploads: true,
debug: false,
restrictions: {
maxFileSize: null,
minFileSize: null,
maxTotalFileSize: null,
maxNumberOfFiles: null,
minNumberOfFiles: null,
allowedFileTypes: null,
requiredMetaFields: []
},
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}`);
if (this.opts.restrictions.allowedFileTypes && this.opts.restrictions.allowedFileTypes !== null && !Array.isArray(this.opts.restrictions.allowedFileTypes)) {
throw new TypeError('`restrictions.allowedFileTypes` must be an array');
}
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, _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, ...args) {
_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(`Cant 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(({
progress
}) => !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)
};
}
/**
* A public wrapper for _checkRestrictions — checks if a file passes a set of restrictions.
* For use in UI pluigins (like Providers), to disallow selecting files that wont pass restrictions.
*
* @param {object} file object to check
* @param {Array} [files] array to check maxNumberOfFiles and maxTotalFileSize
* @returns {object} { result: true/false, reason: why file didnt pass restrictions }
*/
validateRestrictions(file, files) {
try {
_classPrivateFieldLooseBase(this, _checkRestrictions)[_checkRestrictions](file, files);
return {
result: true
};
} catch (err) {
return {
result: false,
reason: err.message
};
}
}
/**
* Check if file passes a set of restrictions set in options: maxFileSize, minFileSize,
* maxNumberOfFiles and allowedFileTypes.
*
* @param {object} file object to check
* @param {Array} [files] array to check maxNumberOfFiles and maxTotalFileSize
* @private
*/
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 = 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 = 'info', 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](...args) {
return _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](...args);
}
/**
* 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, _checkMinNumberOfFiles)[_checkMinNumberOfFiles](files);
_classPrivateFieldLooseBase(this, _checkRequiredMetaFields)[_checkRequiredMetaFields](files);
}).catch(err => {
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](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 => {
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, {
showInformer: false
});
});
}
} // Expose class constructor.
function _checkRestrictions2(file, files = this.getFiles()) {
const {
maxFileSize,
minFileSize,
maxTotalFileSize,
maxNumberOfFiles,
allowedFileTypes
} = this.opts.restrictions;
if (maxNumberOfFiles) {
if (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.indexOf('/') > -1) {
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) {
let totalFilesSize = 0;
totalFilesSize += file.size;
files.forEach(f => {
totalFilesSize += f.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) {
if (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) {
if (file.size < minFileSize) {
throw new RestrictionError(this.i18n('inferiorSize', {
size: prettierBytes(minFileSize)
}));
}
}
}
function _checkMinNumberOfFiles2(files) {
const {
minNumberOfFiles
} = this.opts.restrictions;
if (Object.keys(files).length < minNumberOfFiles) {
throw new RestrictionError(`${this.i18n('youHaveToAtLeastSelectX', {
smart_count: minNumberOfFiles
})}`);
}
}
function _checkRequiredMetaFields2(files) {
const {
requiredMetaFields
} = this.opts.restrictions;
const {
hasOwnProperty
} = Object.prototype.hasOwnProperty;
const errors = [];
const fileIDs = Object.keys(files);
for (let i = 0; i < fileIDs.length; i++) {
const file = this.getFile(fileIDs[i]);
for (let i = 0; i < requiredMetaFields.length; i++) {
if (!hasOwnProperty.call(file.meta, requiredMetaFields[i])) {
const err = new RestrictionError(`${this.i18n('missingRequiredMetaFieldOnFile', {
fileName: file.name
})}`);
errors.push(err);
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, {
file,
showInformer: false,
throwErr: false
});
}
}
}
if (errors.length) {
throw new AggregateRestrictionError(`${this.i18n('missingRequiredMetaField')}`, errors);
}
}
function _showOrLogErrorAndThrow2(err, {
showInformer = true,
file = null,
throwErr = true
} = {}) {
const message = typeof err === 'object' ? err.message : err;
const details = typeof err === 'object' && err.details ? err.details : ''; // Restriction errors should be logged, but not as errors,
// as they are expected and shown in the UI.
let logMessageWithDetails = message;
if (details) {
logMessageWithDetails += ` ${details}`;
}
if (err.isRestriction) {
this.log(logMessageWithDetails);
this.emit('restriction-failed', file, err);
} else {
this.log(logMessageWithDetails, 'error');
} // Sometimes informer has to be shown manually by the developer,
// for example, in `onBeforeFileAdded`.
if (showInformer) {
this.info({
message,
details
}, 'error', this.opts.infoTimeout);
}
if (throwErr) {
throw typeof err === 'object' ? err : new Error(err);
}
}
function _assertNewUploadAllowed2(file) {
const {
allowNewUpload
} = this.getState();
if (allowNewUpload === false) {
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](new RestrictionError(this.i18n('noNewAlreadyUploading')), {
file
});
}
}
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, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](error, {
file: fileDescriptor
});
}
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) {
// Dont show UI info for this error, as it should be done by the developer
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](new RestrictionError('Cannot add the file because onBeforeFileAdded returned false.'), {
showInformer: false,
fileDescriptor
});
} else if (typeof onBeforeFileAddedResult === 'object' && onBeforeFileAddedResult !== null) {
newFile = onBeforeFileAddedResult;
}
try {
const filesArray = Object.keys(files).map(i => files[i]);
_classPrivateFieldLooseBase(this, _checkRestrictions)[_checkRestrictions](newFile, filesArray);
} catch (err) {
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, {
file: newFile
});
}
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, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](newError, {
throwErr: false
});
} else {
_classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](error, {
throwErr: false
});
}
});
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();
}); // 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 = {}) {
// 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 = cuid();
this.emit('upload', {
id: uploadID,
fileIDs
});
this.setState({
allowNewUpload: 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
});
}
function _runUpload2(uploadID) {
const uploadData = this.getState().currentUploads[uploadID];
const restoreStep = uploadData.step;
const steps = [..._classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors], ..._classPrivateFieldLooseBase(this, _uploaders)[_uploaders], ..._classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors]];
let lastStep = Promise.resolve();
steps.forEach((fn, step) => {
// Skip this step if we are restoring and have already completed this step before.
if (step < restoreStep) {
return;
}
lastStep = lastStep.then(() => {
const {
currentUploads
} = this.getState();
const currentUpload = currentUploads[uploadID];
if (!currentUpload) {
return;
}
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
return fn(updatedUpload.fileIDs, uploadID); // eslint-disable-line consistent-return
}).then(() => null);
}); // Not returning the `catch`ed promise, because we still want to return a rejected
// promise from this method if the upload failed.
lastStep.catch(err => {
this.emit('error', err);
_classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID);
});
return lastStep.then(() => {
// Set result data.
const {
currentUploads
} = this.getState();
const currentUpload = currentUploads[uploadID];
if (!currentUpload) {
return;
} // 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);
this.addResultData(uploadID, {
successful,
failed,
uploadID
});
}).then(() => {
// 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.
const {
currentUploads
} = this.getState();
if (!currentUploads[uploadID]) {
return;
}
const currentUpload = currentUploads[uploadID];
const {
result
} = currentUpload;
this.emit('complete', result);
_classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID); // eslint-disable-next-line consistent-return
return result;
}).then(result => {
if (result == null) {
this.log(`Not setting result for an upload that has been removed: ${uploadID}`);
}
return result;
});
}
Uppy.VERSION = require('../package.json').version;
module.exports = Uppy;
module.exports.Uppy = Uppy;
module.exports.UIPlugin = UIPlugin;
module.exports.BasePlugin = BasePlugin;
module.exports.debugLogger = debugLogger;
},{"../package.json":74,"./BasePlugin":57,"./UIPlugin":58,"./getFileName":59,"./loggers":61,"./supportsUploadProgress":62,"@transloadit/prettier-bytes":2,"@uppy/store-default":63,"@uppy/utils/lib/Translator":65,"@uppy/utils/lib/generateFileID":67,"@uppy/utils/lib/getFileNameAndExtension":68,"@uppy/utils/lib/getFileType":69,"cuid":3,"lodash.throttle":7,"mime-match":8,"namespace-emitter":9}],61:[function(require,module,exports){
"use strict";
const getTimeStamp = require('@uppy/utils/lib/getTimeStamp'); // Swallow all logs, except errors.
// default if logger is not set or debug: false
const justErrorsLogger = {
debug: (...args) => {},
warn: (...args) => {},
error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args)
}; // Print logs to console with namespace + timestamp,
// set by logger: Uppy.debugLogger or debug: true
const debugLogger = {
debug: (...args) => console.debug(`[Uppy] [${getTimeStamp()}]`, ...args),
warn: (...args) => console.warn(`[Uppy] [${getTimeStamp()}]`, ...args),
error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args)
};
module.exports = {
justErrorsLogger,
debugLogger
};
},{"@uppy/utils/lib/getTimeStamp":70}],62:[function(require,module,exports){
"use strict";
// 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;
};
},{}],63:[function(require,module,exports){
"use strict";
/**
* Default store that keeps state in a simple object.
*/
class DefaultStore {
constructor() {
this.state = {};
this.callbacks = [];
}
getState() {
return this.state;
}
setState(patch) {
const prevState = { ...this.state
};
const nextState = { ...this.state,
...patch
};
this.state = nextState;
this._publish(prevState, nextState, patch);
}
subscribe(listener) {
this.callbacks.push(listener);
return () => {
// Remove the listener.
this.callbacks.splice(this.callbacks.indexOf(listener), 1);
};
}
_publish(...args) {
this.callbacks.forEach(listener => {
listener(...args);
});
}
}
DefaultStore.VERSION = require('../package.json').version;
module.exports = function defaultStore() {
return new DefaultStore();
};
},{"../package.json":64}],64:[function(require,module,exports){
module.exports={
"name": "@uppy/store-default",
"description": "The default simple object-based store for Uppy.",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"uppy",
"uppy-store"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
}
}
},{}],65:[function(require,module,exports){
arguments[4][48][0].apply(exports,arguments)
},{"./hasProperty":71,"dup":48}],66:[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 = document) {
if (typeof element === 'string') {
return context.querySelector(element);
}
if (isDOMElement(element)) {
return element;
}
};
},{"./isDOMElement":72}],67:[function(require,module,exports){
"use strict";
/**
* 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;
};
function encodeFilename(name) {
let suffix = '';
return name.replace(/[^A-Z0-9]/ig, character => {
suffix += `-${encodeCharacter(character)}`;
return '/';
}) + suffix;
}
function encodeCharacter(character) {
return character.charCodeAt(0).toString(32);
}
},{}],68:[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)
};
};
},{}],69:[function(require,module,exports){
"use strict";
const getFileNameAndExtension = require('./getFileNameAndExtension');
const mimeTypes = require('./mimeTypes');
module.exports = function getFileType(file) {
let fileExtension = file.name ? getFileNameAndExtension(file.name).extension : null;
fileExtension = fileExtension ? fileExtension.toLowerCase() : null;
if (file.type) {
// if mime type is set in the file object already, use that
return file.type;
}
if (fileExtension && mimeTypes[fileExtension]) {
// 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":68,"./mimeTypes":73}],70:[function(require,module,exports){
"use strict";
/**
* Returns a timestamp in the format of `hours:minutes:seconds`
*/
module.exports = function getTimeStamp() {
const date = new Date();
const hours = pad(date.getHours().toString());
const minutes = pad(date.getMinutes().toString());
const seconds = pad(date.getSeconds().toString());
return `${hours}:${minutes}:${seconds}`;
};
/**
* Adds zero to strings shorter than two characters
*/
function pad(str) {
return str.length !== 2 ? 0 + str : str;
}
},{}],71:[function(require,module,exports){
arguments[4][53][0].apply(exports,arguments)
},{"dup":53}],72:[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 && typeof obj === 'object' && obj.nodeType === Node.ELEMENT_NODE;
};
},{}],73:[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'
};
},{}],74:[function(require,module,exports){
module.exports={
"name": "@uppy/core",
"description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"style": "dist/style.min.css",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"uppy",
"uppy-plugin"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@transloadit/prettier-bytes": "0.0.7",
"@uppy/store-default": "file:../store-default",
"@uppy/utils": "file:../utils",
"exorcist": "^2.0.0",
"lodash.throttle": "^4.1.1",
"mime-match": "^1.0.2",
"namespace-emitter": "^2.0.1",
"cuid": "^2.1.1",
"preact": "^10.5.13"
}
}
},{}],75:[function(require,module,exports){
"use strict";
var _class, _temp;
const {
BasePlugin
} = require('@uppy/core');
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.handleDrop = event => {
event.preventDefault();
event.stopPropagation();
clearTimeout(this.removeDragOverClassTimeout); // 2. Remove dragover class
event.currentTarget.classList.remove('uppy-is-drag-over');
this.setPluginState({
isDraggingOver: false
}); // 3. Add all dropped files
this.uppy.log('[DropTarget] Files were dropped');
const logDropError = error => {
this.uppy.log(error, 'error');
};
getDroppedFiles(event.dataTransfer, {
logDropError
}).then(files => this.addFiles(files));
};
this.handleDragOver = event => {
event.preventDefault();
event.stopPropagation(); // 1. 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.handleDragLeave = event => {
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.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 = require('../package.json').version, _temp);
},{"../package.json":82,"@uppy/core":60,"@uppy/utils/lib/getDroppedFiles":76,"@uppy/utils/lib/toArray":81}],76:[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, {
logDropError = () => {}
} = {}) {
// Get all files from all subdirs. Works (at least) in Chrome, Mozilla, and Safari
if (dataTransfer.items && dataTransfer.items[0] && 'webkitGetAsEntry' in dataTransfer.items[0]) {
return webkitGetAsEntryApi(dataTransfer, logDropError); // Otherwise just return all first-order files
}
return fallbackApi(dataTransfer);
};
},{"./utils/fallbackApi":77,"./utils/webkitGetAsEntryApi/index":80}],77:[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":81}],78:[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, {
onSuccess
}) {
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);
});
};
},{}],79:[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;
};
},{}],80:[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 => {
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 => {
const promises = entries.map(entry => createPromiseToAddFileOrParseDirectory(entry));
Promise.all(promises).then(() => resolve());
}
});
}
}); // 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":81,"./getFilesAndDirectoriesFromDirectory":78,"./getRelativePath":79}],81:[function(require,module,exports){
"use strict";
/**
* Converts list into array
*/
module.exports = function toArray(list) {
return Array.prototype.slice.call(list || [], 0);
};
},{}],82:[function(require,module,exports){
module.exports={
"name": "@uppy/drop-target",
"description": "Lets your users drag and drop files on a DOM element",
"version": "1.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"uppy",
"uppy-plugin",
"drag-drop",
"drag",
"drop",
"dropzone",
"upload"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@uppy/utils": "file:../utils"
},
"peerDependencies": {
"@uppy/core": "^1.0.0"
},
"publishConfig": {
"access": "public"
}
}
},{}],83:[function(require,module,exports){
"use strict";
var _class, _temp;
const {
BasePlugin
} = require('@uppy/core');
const cuid = require('cuid');
const Translator = require('@uppy/utils/lib/Translator');
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/src/RateLimitedQueue');
const NetworkError = require('@uppy/utils/lib/NetworkError');
const isNetworkError = require('@uppy/utils/lib/isNetworkError');
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 = {
strings: {
timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
}
}; // 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) {
this.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.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 = cuid();
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) => {
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('pause', {});
queuedRequest.abort();
resolve(`upload ${file.id} was removed`);
});
this.onCancelAll(file.id, () => {
socket.send('pause', {});
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 dont support remote uploads
const isSomeFileRemote = files.some(file => file.isRemote);
if (isSomeFileRemote) {
throw new Error('Cant 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 = require('../package.json').version, _temp);
},{"../package.json":104,"@uppy/companion-client":89,"@uppy/core":60,"@uppy/utils/lib/EventTracker":92,"@uppy/utils/lib/NetworkError":93,"@uppy/utils/lib/ProgressTimeout":94,"@uppy/utils/lib/Translator":95,"@uppy/utils/lib/emitSocketProgress":96,"@uppy/utils/lib/getSocketHost":98,"@uppy/utils/lib/isNetworkError":100,"@uppy/utils/lib/settle":101,"@uppy/utils/src/RateLimitedQueue":102,"cuid":3}],84:[function(require,module,exports){
arguments[4][15][0].apply(exports,arguments)
},{"dup":15}],85:[function(require,module,exports){
arguments[4][16][0].apply(exports,arguments)
},{"./RequestClient":86,"./tokenStorage":90,"dup":16,"qs-stringify":11}],86:[function(require,module,exports){
arguments[4][17][0].apply(exports,arguments)
},{"../package.json":91,"./AuthError":84,"@uppy/utils/lib/fetchWithNetworkError":97,"dup":17}],87:[function(require,module,exports){
arguments[4][18][0].apply(exports,arguments)
},{"./RequestClient":86,"dup":18}],88:[function(require,module,exports){
arguments[4][19][0].apply(exports,arguments)
},{"dup":19,"namespace-emitter":9}],89:[function(require,module,exports){
arguments[4][20][0].apply(exports,arguments)
},{"./Provider":85,"./RequestClient":86,"./SearchProvider":87,"./Socket":88,"dup":20}],90:[function(require,module,exports){
arguments[4][21][0].apply(exports,arguments)
},{"dup":21}],91:[function(require,module,exports){
arguments[4][22][0].apply(exports,arguments)
},{"dup":22}],92:[function(require,module,exports){
arguments[4][24][0].apply(exports,arguments)
},{"dup":24}],93:[function(require,module,exports){
arguments[4][25][0].apply(exports,arguments)
},{"dup":25}],94:[function(require,module,exports){
arguments[4][46][0].apply(exports,arguments)
},{"dup":46}],95:[function(require,module,exports){
arguments[4][48][0].apply(exports,arguments)
},{"./hasProperty":99,"dup":48}],96:[function(require,module,exports){
arguments[4][28][0].apply(exports,arguments)
},{"dup":28,"lodash.throttle":7}],97:[function(require,module,exports){
arguments[4][29][0].apply(exports,arguments)
},{"./NetworkError":93,"dup":29}],98:[function(require,module,exports){
arguments[4][31][0].apply(exports,arguments)
},{"dup":31}],99:[function(require,module,exports){
arguments[4][53][0].apply(exports,arguments)
},{"dup":53}],100:[function(require,module,exports){
arguments[4][54][0].apply(exports,arguments)
},{"dup":54}],101:[function(require,module,exports){
arguments[4][55][0].apply(exports,arguments)
},{"dup":55}],102:[function(require,module,exports){
const findIndex = require('./findIndex')
function createCancelError () {
return new Error('Cancelled')
}
class RateLimitedQueue {
constructor (limit) {
if (typeof limit !== 'number' || limit === 0) {
this.limit = Infinity
} else {
this.limit = limit
}
this.activeRequests = 0
this.queuedHandlers = []
}
_call (fn) {
this.activeRequests += 1
let done = false
let cancelActive
try {
cancelActive = fn()
} catch (err) {
this.activeRequests -= 1
throw err
}
return {
abort: () => {
if (done) return
done = true
this.activeRequests -= 1
cancelActive()
this._queueNext()
},
done: () => {
if (done) return
done = true
this.activeRequests -= 1
this._queueNext()
},
}
}
_queueNext () {
// 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)
Promise.resolve().then(() => {
this._next()
})
}
_next () {
if (this.activeRequests >= this.limit) {
return
}
if (this.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 = this.queuedHandlers.shift()
const handler = this._call(next.fn)
next.abort = handler.abort
next.done = handler.done
}
_queue (fn, options = {}) {
const handler = {
fn,
priority: options.priority || 0,
abort: () => {
this._dequeue(handler)
},
done: () => {
throw new Error('Cannot mark a queued request as done: this indicates a bug')
},
}
const index = findIndex(this.queuedHandlers, (other) => {
return handler.priority > other.priority
})
if (index === -1) {
this.queuedHandlers.push(handler)
} else {
this.queuedHandlers.splice(index, 0, handler)
}
return handler
}
_dequeue (handler) {
const index = this.queuedHandlers.indexOf(handler)
if (index !== -1) {
this.queuedHandlers.splice(index, 1)
}
}
run (fn, queueOptions) {
if (this.activeRequests < this.limit) {
return this._call(fn)
}
return this._queue(fn, queueOptions)
}
wrapPromiseFunction (fn, queueOptions) {
return (...args) => {
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
}
}
}
module.exports = {
RateLimitedQueue,
internalRateLimitedQueue: Symbol('__queue'),
}
},{"./findIndex":103}],103:[function(require,module,exports){
/**
* Array.prototype.findIndex ponyfill for old browsers.
*
* @param {Array} array
* @param {Function} predicate
* @returns {number}
*/
module.exports = function findIndex (array, predicate) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) return i
}
return -1
}
},{}],104:[function(require,module,exports){
module.exports={
"name": "@uppy/xhr-upload",
"description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.",
"version": "2.0.0-alpha.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"keywords": [
"file uploader",
"xhr",
"xhr upload",
"XMLHttpRequest",
"ajax",
"fetch",
"uppy",
"uppy-plugin"
],
"homepage": "https://uppy.io",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/transloadit/uppy.git"
},
"dependencies": {
"@uppy/companion-client": "file:../companion-client",
"@uppy/utils": "file:../utils",
"cuid": "^2.1.1"
},
"devDependencies": {
"nock": "^13.1.0"
},
"peerDependencies": {
"@uppy/core": "^1.0.0"
}
}
},{}]},{},[1]);