UNPKG

uppy

Version:

Almost as cute as a Puppy :dog:

1,041 lines (842 loc) 30.5 kB
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Utils = require('../core/Utils'); var Translator = require('../core/Translator'); var UppySocket = require('./UppySocket'); var ee = require('namespace-emitter'); var cuid = require('cuid'); var throttle = require('lodash.throttle'); var prettyBytes = require('prettier-bytes'); var match = require('mime-match' // const deepFreeze = require('deep-freeze-strict') /** * Main Uppy core * * @param {object} opts general options, like locales, to show modal or not to show */ ); var Uppy = function () { function Uppy(opts) { _classCallCheck(this, Uppy); var defaultLocale = { strings: { 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: 'This file exceeds maximum allowed size of', youCanOnlyUploadFileTypes: 'You can only upload:', uppyServerError: 'Connection with Uppy Server failed' } // set default options };var defaultOptions = { id: 'uppy', autoProceed: true, debug: false, restrictions: { maxFileSize: false, maxNumberOfFiles: false, minNumberOfFiles: false, allowedFileTypes: false }, meta: {}, onBeforeFileAdded: function onBeforeFileAdded(currentFile, files) { return Promise.resolve(); }, onBeforeUpload: function onBeforeUpload(files, done) { return Promise.resolve(); }, locale: defaultLocale // Merge default options with the ones set by user };this.opts = _extends({}, defaultOptions, opts // // Dictates in what order different plugin types are ran: // this.types = [ 'presetter', 'orchestrator', 'progressindicator', // 'acquirer', 'modifier', 'uploader', 'presenter', 'debugger'] );this.locale = _extends({}, defaultLocale, this.opts.locale); this.locale.strings = _extends({}, defaultLocale.strings, this.opts.locale.strings // i18n );this.translator = new Translator({ locale: this.locale }); this.i18n = this.translator.translate.bind(this.translator // Container for different types of plugins );this.plugins = {}; // @TODO maybe bindall this.translator = new Translator({ locale: this.opts.locale }); this.i18n = this.translator.translate.bind(this.translator); this.getState = this.getState.bind(this); this.updateMeta = this.updateMeta.bind(this); this.initSocket = this.initSocket.bind(this); this.log = this.log.bind(this); this.info = this.info.bind(this); this.hideInfo = this.hideInfo.bind(this); this.addFile = this.addFile.bind(this); this.removeFile = this.removeFile.bind(this); this.calculateProgress = this.calculateProgress.bind(this); this.resetProgress = this.resetProgress.bind(this // this.bus = this.emitter = ee() );this.emitter = ee(); this.on = this.emitter.on.bind(this.emitter); this.off = this.emitter.off.bind(this.emitter); this.once = this.emitter.once.bind(this.emitter); this.emit = this.emitter.emit.bind(this.emitter); this.preProcessors = []; this.uploaders = []; this.postProcessors = []; this.state = { files: {}, capabilities: { resumableUploads: false }, totalProgress: 0, meta: _extends({}, this.opts.meta), info: { isHidden: true, type: 'info', message: '' } // for debugging and testing };this.updateNum = 0; if (this.opts.debug) { global.UppyState = this.state; global.uppyLog = ''; // global.UppyAddFile = this.addFile.bind(this) global._uppy = this; } } /** * Iterate on all plugins and run `update` on them. Called each time state changes * */ Uppy.prototype.updateAll = function updateAll(state) { this.iteratePlugins(function (plugin) { plugin.update(state); }); }; /** * Updates state * * @param {patch} object */ Uppy.prototype.setState = function setState(patch) { var prevState = _extends({}, this.state); var nextState = _extends({}, this.state, patch); this.state = nextState; this.emit('core:state-update', prevState, nextState, patch); this.updateAll(this.state); }; /** * Returns current state * */ Uppy.prototype.getState = function getState() { // use deepFreeze for debugging // return deepFreeze(this.state) return this.state; }; Uppy.prototype.reset = function reset() { this.emit('core:pause-all'); this.emit('core:cancel-all'); this.setState({ totalProgress: 0 }); }; Uppy.prototype.resetProgress = function resetProgress() { var defaultProgress = { percentage: 0, bytesUploaded: 0, uploadComplete: false, uploadStarted: false }; var files = _extends({}, this.state.files); var updatedFiles = {}; Object.keys(files).forEach(function (fileID) { var updatedFile = _extends({}, files[fileID]); updatedFile.progress = _extends({}, updatedFile.progress, defaultProgress); updatedFiles[fileID] = updatedFile; }); this.setState({ files: updatedFiles, totalProgress: 0 } // TODO Document on the website );this.emit('core:reset-progress'); }; Uppy.prototype.addPreProcessor = function addPreProcessor(fn) { this.preProcessors.push(fn); }; Uppy.prototype.removePreProcessor = function removePreProcessor(fn) { var i = this.preProcessors.indexOf(fn); if (i !== -1) { this.preProcessors.splice(i, 1); } }; Uppy.prototype.addPostProcessor = function addPostProcessor(fn) { this.postProcessors.push(fn); }; Uppy.prototype.removePostProcessor = function removePostProcessor(fn) { var i = this.postProcessors.indexOf(fn); if (i !== -1) { this.postProcessors.splice(i, 1); } }; Uppy.prototype.addUploader = function addUploader(fn) { this.uploaders.push(fn); }; Uppy.prototype.removeUploader = function removeUploader(fn) { var i = this.uploaders.indexOf(fn); if (i !== -1) { this.uploaders.splice(i, 1); } }; Uppy.prototype.setMeta = function setMeta(data) { var newMeta = _extends({}, this.getState().meta, data); this.log('Adding metadata:'); this.log(data); this.setState({ meta: newMeta }); }; Uppy.prototype.updateMeta = function updateMeta(data, fileID) { var updatedFiles = _extends({}, this.getState().files); var newMeta = _extends({}, updatedFiles[fileID].meta, data); updatedFiles[fileID] = _extends({}, updatedFiles[fileID], { meta: newMeta }); this.setState({ files: updatedFiles }); }; /** * Check if minNumberOfFiles restriction is reached before uploading * * @return {boolean} * @private */ Uppy.prototype.checkMinNumberOfFiles = function checkMinNumberOfFiles() { var minNumberOfFiles = this.opts.restrictions.minNumberOfFiles; if (Object.keys(this.state.files).length < minNumberOfFiles) { this.info('' + this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles }), 'error', 5000); return false; } return true; }; /** * Check if file passes a set of restrictions set in options: maxFileSize, * maxNumberOfFiles and allowedFileTypes * * @param {object} file object to check * @return {boolean} * @private */ Uppy.prototype.checkRestrictions = function checkRestrictions(file) { var _opts$restrictions = this.opts.restrictions, maxFileSize = _opts$restrictions.maxFileSize, maxNumberOfFiles = _opts$restrictions.maxNumberOfFiles, allowedFileTypes = _opts$restrictions.allowedFileTypes; if (maxNumberOfFiles) { if (Object.keys(this.state.files).length + 1 > maxNumberOfFiles) { this.info('' + this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles }), 'error', 5000); return false; } } if (allowedFileTypes) { var isCorrectFileType = allowedFileTypes.filter(match(file.type.mime)).length > 0; if (!isCorrectFileType) { var allowedFileTypesString = allowedFileTypes.join(', '); this.info(this.i18n('youCanOnlyUploadFileTypes') + ' ' + allowedFileTypesString, 'error', 5000); return false; } } if (maxFileSize) { if (file.data.size > maxFileSize) { this.info(this.i18n('exceedsSize') + ' ' + prettyBytes(maxFileSize), 'error', 5000); return false; } } return true; }; /** * 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 */ Uppy.prototype.addFile = function addFile(file) { var _this = this; // Wrap this in a Promise `.then()` handler so errors will reject the Promise // instead of throwing. var beforeFileAdded = Promise.resolve().then(function () { return _this.opts.onBeforeFileAdded(file, _this.getState().files); }); return beforeFileAdded.catch(function (err) { _this.info(err, 'error', 5000); return Promise.reject('onBeforeFileAdded: ' + err); }).then(function () { return Utils.getFileType(file).then(function (fileType) { var updatedFiles = _extends({}, _this.state.files); var fileName = file.name || 'noname'; var fileExtension = Utils.getFileNameAndExtension(fileName)[1]; var isRemote = file.isRemote || false; var fileID = Utils.generateFileID(file); var fileTypeGeneral = fileType[0]; var fileTypeSpecific = fileType[1]; var newFile = { source: file.source || '', id: fileID, name: fileName, extension: fileExtension || '', meta: _extends({}, { name: fileName }, _this.getState().meta), type: { general: fileTypeGeneral, specific: fileTypeSpecific, mime: fileType.join('/') }, data: file.data, progress: { percentage: 0, bytesUploaded: 0, bytesTotal: file.data.size || 0, uploadComplete: false, uploadStarted: false }, size: file.data.size || 'N/A', isRemote: isRemote, remote: file.remote || '', preview: file.preview }; var isFileAllowed = _this.checkRestrictions(newFile); if (!isFileAllowed) return Promise.reject('File not allowed'); updatedFiles[fileID] = newFile; _this.setState({ files: updatedFiles }); _this.emit('core:file-added', newFile); _this.log('Added file: ' + fileName + ', ' + fileID + ', mime type: ' + fileType); if (_this.opts.autoProceed && !_this.scheduledAutoProceed) { _this.scheduledAutoProceed = setTimeout(function () { _this.scheduledAutoProceed = null; _this.upload().catch(function (err) { console.error(err.stack || err.message || err); }); }, 4); } }); }); }; /** * Get a file object. * * @param {string} fileID The ID of the file object to return. */ Uppy.prototype.getFile = function getFile(fileID) { return this.getState().files[fileID]; }; /** * Generate a preview image for the given file, if possible. */ Uppy.prototype.generatePreview = function generatePreview(file) { var _this2 = this; if (Utils.isPreviewSupported(file.type.specific) && !file.isRemote) { Utils.createThumbnail(file, 200).then(function (thumbnail) { _this2.setPreviewURL(file.id, thumbnail); }).catch(function (err) { console.warn(err.stack || err.message); }); } }; /** * Set the preview URL for a file. */ Uppy.prototype.setPreviewURL = function setPreviewURL(fileID, preview) { var _extends2; var files = this.state.files; this.setState({ files: _extends({}, files, (_extends2 = {}, _extends2[fileID] = _extends({}, files[fileID], { preview: preview }), _extends2)) }); }; Uppy.prototype.removeFile = function removeFile(fileID) { var updatedFiles = _extends({}, this.getState().files); var removedFile = updatedFiles[fileID]; delete updatedFiles[fileID]; this.setState({ files: updatedFiles }); this.calculateTotalProgress(); this.emit('core:file-removed', fileID // Clean up object URLs. );if (removedFile.preview && Utils.isObjectURL(removedFile.preview)) { URL.revokeObjectURL(removedFile.preview); } this.log('Removed file: ' + fileID); }; Uppy.prototype.calculateProgress = function calculateProgress(data) { var fileID = data.id; var updatedFiles = _extends({}, this.getState().files // skip progress event for a file that’s been removed );if (!updatedFiles[fileID]) { this.log('Trying to set progress for a file that’s not with us anymore: ', fileID); return; } var updatedFile = _extends({}, updatedFiles[fileID], _extends({}, { progress: _extends({}, updatedFiles[fileID].progress, { bytesUploaded: data.bytesUploaded, bytesTotal: data.bytesTotal, percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2)) }) })); updatedFiles[data.id] = updatedFile; this.setState({ files: updatedFiles }); this.calculateTotalProgress(); }; Uppy.prototype.calculateTotalProgress = function calculateTotalProgress() { // calculate total progress, using the number of files currently uploading, // multiplied by 100 and the summ of individual progress of each file var files = _extends({}, this.getState().files); var inProgress = Object.keys(files).filter(function (file) { return files[file].progress.uploadStarted; }); var progressMax = inProgress.length * 100; var progressAll = 0; inProgress.forEach(function (file) { progressAll = progressAll + files[file].progress.percentage; }); var totalProgress = progressMax === 0 ? 0 : Math.floor((progressAll * 100 / progressMax).toFixed(2)); this.setState({ totalProgress: totalProgress }); }; /** * Registers listeners for all global actions, like: * `file-add`, `file-remove`, `upload-progress`, `reset` * */ Uppy.prototype.actions = function actions() { var _this3 = this; // this.bus.on('*', (payload) => { // console.log('emitted: ', this.event) // console.log('with payload: ', payload) // }) // stress-test re-rendering // setInterval(() => { // this.setState({bla: 'bla'}) // }, 20) this.on('core:error', function (error) { _this3.setState({ error: error }); }); this.on('core:upload-error', function (fileID, error) { var fileName = _this3.state.files[fileID].name; var message = 'Failed to upload ' + fileName; if ((typeof error === 'undefined' ? 'undefined' : _typeof(error)) === 'object' && error.message) { message = message + ': ' + error.message; } _this3.info(message, 'error', 5000); }); this.on('core:upload', function () { _this3.setState({ error: null }); }); this.on('core:file-add', function (data) { _this3.addFile(data); }); this.on('core:file-added', function (file) { _this3.generatePreview(file); } // `remove-file` removes a file from `state.files`, for example when // a user decides not to upload particular file and clicks a button to remove it );this.on('core:file-remove', function (fileID) { _this3.removeFile(fileID); }); this.on('core:cancel-all', function () { // let updatedFiles = this.getState().files // updatedFiles = {} _this3.setState({ files: {} }); }); this.on('core:upload-started', function (fileID, upload) { var updatedFiles = _extends({}, _this3.getState().files); var updatedFile = _extends({}, updatedFiles[fileID], _extends({}, { progress: _extends({}, updatedFiles[fileID].progress, { uploadStarted: Date.now() }) })); updatedFiles[fileID] = updatedFile; _this3.setState({ files: updatedFiles }); } // upload progress events can occur frequently, especially when you have a good // connection to the remote server. Therefore, we are throtteling them to // prevent accessive function calls. // see also: https://github.com/tus/tus-js-client/commit/9940f27b2361fd7e10ba58b09b60d82422183bbb );var throttledCalculateProgress = throttle(this.calculateProgress, 100, { leading: true, trailing: false }); this.on('core:upload-progress', function (data) { // this.calculateProgress(data) throttledCalculateProgress(data); }); this.on('core:upload-success', function (fileID, uploadResp, uploadURL) { var updatedFiles = _extends({}, _this3.getState().files); var updatedFile = _extends({}, updatedFiles[fileID], { progress: _extends({}, updatedFiles[fileID].progress, { uploadComplete: true, // good or bad idea? setting the percentage to 100 if upload is successful, // so that if we lost some progress events on the way, its still marked “compete”? percentage: 100 }), uploadURL: uploadURL }); updatedFiles[fileID] = updatedFile; _this3.setState({ files: updatedFiles }); _this3.calculateTotalProgress(); }); this.on('core:update-meta', function (data, fileID) { _this3.updateMeta(data, fileID); }); this.on('core:preprocess-progress', function (fileID, progress) { var files = _extends({}, _this3.getState().files); files[fileID] = _extends({}, files[fileID], { progress: _extends({}, files[fileID].progress, { preprocess: progress }) }); _this3.setState({ files: files }); }); this.on('core:preprocess-complete', function (fileID) { var files = _extends({}, _this3.getState().files); files[fileID] = _extends({}, files[fileID], { progress: _extends({}, files[fileID].progress) }); delete files[fileID].progress.preprocess; _this3.setState({ files: files }); }); this.on('core:postprocess-progress', function (fileID, progress) { var files = _extends({}, _this3.getState().files); files[fileID] = _extends({}, files[fileID], { progress: _extends({}, files[fileID].progress, { postprocess: progress }) }); _this3.setState({ files: files }); }); this.on('core:postprocess-complete', function (fileID) { var files = _extends({}, _this3.getState().files); files[fileID] = _extends({}, files[fileID], { progress: _extends({}, files[fileID].progress) }); delete files[fileID].progress.postprocess; // TODO should we set some kind of `fullyComplete` property on the file object // so it's easier to see that the file is upload…fully complete…rather than // what we have to do now (`uploadComplete && !postprocess`) _this3.setState({ files: files }); } // show informer if offline );if (typeof window !== 'undefined') { window.addEventListener('online', function () { return _this3.updateOnlineStatus(); }); window.addEventListener('offline', function () { return _this3.updateOnlineStatus(); }); setTimeout(function () { return _this3.updateOnlineStatus(); }, 3000); } }; Uppy.prototype.updateOnlineStatus = function updateOnlineStatus() { var online = typeof window.navigator.onLine !== 'undefined' ? window.navigator.onLine : true; if (!online) { this.emit('is-offline'); this.info('No internet connection', 'error', 0); this.wasOffline = true; } else { this.emit('is-online'); if (this.wasOffline) { this.emit('back-online'); this.info('Connected!', 'success', 3000); this.wasOffline = false; } } }; Uppy.prototype.getID = function getID() { return this.opts.id; }; /** * Registers a plugin with Core * * @param {Class} Plugin object * @param {Object} options object that will be passed to Plugin later * @return {Object} self for chaining */ Uppy.prototype.use = function use(Plugin, opts) { if (typeof Plugin !== 'function') { var msg = 'Expected a plugin class, but got ' + (Plugin === null ? 'null' : typeof Plugin === 'undefined' ? 'undefined' : _typeof(Plugin)) + '.' + ' Please verify that the plugin was imported and spelled correctly.'; throw new TypeError(msg); } // Instantiate var plugin = new Plugin(this, opts); var pluginId = plugin.id; this.plugins[plugin.type] = this.plugins[plugin.type] || []; if (!pluginId) { throw new Error('Your plugin must have an id'); } if (!plugin.type) { throw new Error('Your plugin must have a type'); } var existsPluginAlready = this.getPlugin(pluginId); if (existsPluginAlready) { var _msg = 'Already found a plugin named \'' + existsPluginAlready.id + '\'.\n Tried to use: \'' + pluginId + '\'.\n Uppy is currently limited to running one of every plugin.\n Share your use case with us over at\n https://github.com/transloadit/uppy/issues/\n if you want us to reconsider.'; throw new Error(_msg); } this.plugins[plugin.type].push(plugin); plugin.install(); return this; }; /** * Find one Plugin by name * * @param string name description */ Uppy.prototype.getPlugin = function getPlugin(name) { var foundPlugin = false; this.iteratePlugins(function (plugin) { var pluginName = plugin.id; if (pluginName === name) { foundPlugin = plugin; return false; } }); return foundPlugin; }; /** * Iterate through all `use`d plugins * * @param function method description */ Uppy.prototype.iteratePlugins = function iteratePlugins(method) { var _this4 = this; Object.keys(this.plugins).forEach(function (pluginType) { _this4.plugins[pluginType].forEach(method); }); }; /** * Uninstall and remove a plugin. * * @param {Plugin} instance The plugin instance to remove. */ Uppy.prototype.removePlugin = function removePlugin(instance) { var list = this.plugins[instance.type]; if (instance.uninstall) { instance.uninstall(); } var index = list.indexOf(instance); if (index !== -1) { list.splice(index, 1); } }; /** * Uninstall all plugins and close down this Uppy instance. */ Uppy.prototype.close = function close() { this.reset(); this.iteratePlugins(function (plugin) { plugin.uninstall(); }); if (this.socket) { this.socket.close(); } }; /** * Set info message in `state.info`, so that UI plugins like `Informer` * can display the message * * @param {string} msg Message to be displayed by the informer */ Uppy.prototype.info = function info(message, type, duration) { var isComplexMessage = (typeof message === 'undefined' ? 'undefined' : _typeof(message)) === 'object'; this.setState({ info: { isHidden: false, type: type || 'info', message: isComplexMessage ? message.message : message, details: isComplexMessage ? message.details : null } }); this.emit('core:info-visible'); window.clearTimeout(this.infoTimeoutID); if (duration === 0) { this.infoTimeoutID = undefined; return; } // hide the informer after `duration` milliseconds this.infoTimeoutID = setTimeout(this.hideInfo, duration); }; Uppy.prototype.hideInfo = function hideInfo() { var newInfo = _extends({}, this.state.info, { isHidden: true }); this.setState({ info: newInfo }); this.emit('core:info-hidden'); }; /** * Logs stuff to console, only if `debug` is set to true. Silent in production. * * @return {String|Object} to log */ Uppy.prototype.log = function log(msg, type) { if (!this.opts.debug) { return; } if (type === 'error') { console.error('LOG: ' + msg); return; } if (msg === '' + msg) { console.log('LOG: ' + msg); } else { console.dir(msg); } global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg; }; Uppy.prototype.initSocket = function initSocket(opts) { if (!this.socket) { this.socket = new UppySocket(opts); } return this.socket; }; /** * Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options * */ Uppy.prototype.run = function run() { this.log('Core is run, initializing actions...'); this.actions(); return this; }; /** * Restore an upload by its ID. */ Uppy.prototype.restore = function restore(uploadID) { this.log('Core: attempting to restore upload "' + uploadID + '"'); if (!this.state.currentUploads[uploadID]) { this.removeUpload(uploadID); return Promise.reject(new Error('Nonexistent upload')); } return this.runUpload(uploadID); }; /** * Create an upload for a bunch of files. * * @param {Array<string>} fileIDs File IDs to include in this upload. * @return {string} ID of this upload. */ Uppy.prototype.createUpload = function createUpload(fileIDs) { var _extends3; var uploadID = cuid(); this.emit('core:upload', { id: uploadID, fileIDs: fileIDs }); this.setState({ currentUploads: _extends({}, this.state.currentUploads, (_extends3 = {}, _extends3[uploadID] = { fileIDs: fileIDs, step: 0 }, _extends3)) }); return uploadID; }; /** * Remove an upload, eg. if it has been canceled or completed. * * @param {string} uploadID The ID of the upload. */ Uppy.prototype.removeUpload = function removeUpload(uploadID) { var currentUploads = _extends({}, this.state.currentUploads); delete currentUploads[uploadID]; this.setState({ currentUploads: currentUploads }); }; /** * Run an upload. This picks up where it left off in case the upload is being restored. * * @private */ Uppy.prototype.runUpload = function runUpload(uploadID) { var _this5 = this; var uploadData = this.state.currentUploads[uploadID]; var fileIDs = uploadData.fileIDs; var restoreStep = uploadData.step; var steps = [].concat(this.preProcessors, this.uploaders, this.postProcessors); var lastStep = Promise.resolve(); steps.forEach(function (fn, step) { // Skip this step if we are restoring and have already completed this step before. if (step < restoreStep) { return; } lastStep = lastStep.then(function () { var _extends4; var currentUpload = _extends({}, _this5.state.currentUploads[uploadID], { step: step }); _this5.setState({ currentUploads: _extends({}, _this5.state.currentUploads, (_extends4 = {}, _extends4[uploadID] = currentUpload, _extends4)) } // TODO give this the `currentUpload` object as its only parameter maybe? // Otherwise when more metadata may be added to the upload this would keep getting more parameters );return fn(fileIDs, uploadID); }); } // Not returning the `catch`ed promise, because we still want to return a rejected // promise from this method if the upload failed. );lastStep.catch(function (err) { _this5.emit('core:error', err); _this5.removeUpload(uploadID); }); return lastStep.then(function () { _this5.emit('core:success', fileIDs); _this5.removeUpload(uploadID); }); }; /** * Start an upload for all the files that are not currently being uploaded. * * @return {Promise} */ Uppy.prototype.upload = function upload(forceUpload) { var _this6 = this; var isMinNumberOfFilesReached = this.checkMinNumberOfFiles(); if (!isMinNumberOfFilesReached) { return Promise.reject('Minimum number of files has not been reached'); } var beforeUpload = Promise.resolve().then(function () { return _this6.opts.onBeforeUpload(_this6.state.files); }); return beforeUpload.catch(function (err) { _this6.info(err, 'error', 5000); return Promise.reject('onBeforeUpload: ' + err); }).then(function () { var waitingFileIDs = []; Object.keys(_this6.state.files).forEach(function (fileID) { var file = _this6.getFile(fileID // TODO: replace files[file].isRemote with some logic // // filter files that are now yet being uploaded / haven’t been uploaded // and remote too );if (forceUpload) { _this6.resetProgress(); waitingFileIDs.push(file.id); } else if (!file.progress.uploadStarted || file.isRemote) { waitingFileIDs.push(file.id); } }); var uploadID = _this6.createUpload(waitingFileIDs); return _this6.runUpload(uploadID); }); }; return Uppy; }(); module.exports = function (opts) { return new Uppy(opts); }; //# sourceMappingURL=Core.js.map