UNPKG

uppy

Version:

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:

498 lines (404 loc) 14.9 kB
'use strict'; 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"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _Promise = typeof Promise === 'undefined' ? require('es6-promise').Promise : Promise; var Plugin = require('../core/Plugin'); var tus = require('tus-js-client'); var UppySocket = require('../core/UppySocket'); var _require = require('../core/Utils'), emitSocketProgress = _require.emitSocketProgress, getSocketHost = _require.getSocketHost, settle = _require.settle; require('whatwg-fetch'); // Extracted from https://github.com/tus/tus-js-client/blob/master/lib/upload.js#L13 // excepted we removed 'fingerprint' key to avoid adding more dependencies var tusDefaultOptions = { endpoint: '', resume: true, onProgress: null, onChunkComplete: null, onSuccess: null, onError: null, headers: {}, chunkSize: Infinity, withCredentials: false, uploadUrl: null, uploadSize: null, overridePatchMethod: false, retryDelays: null /** * Create a wrapper around an event emitter with a `remove` method to remove * all events that were added using the wrapped emitter. */ };function createEventTracker(emitter) { var events = []; return { on: function on(event, fn) { events.push([event, fn]); return emitter.on(event, fn); }, remove: function remove() { events.forEach(function (_ref) { var event = _ref[0], fn = _ref[1]; emitter.off(event, fn); }); } }; } /** * Tus resumable file uploader * */ module.exports = function (_Plugin) { _inherits(Tus, _Plugin); function Tus(uppy, opts) { _classCallCheck(this, Tus); var _this = _possibleConstructorReturn(this, _Plugin.call(this, uppy, opts)); _this.type = 'uploader'; _this.id = 'Tus'; _this.title = 'Tus'; // set default options var defaultOptions = { resume: true, autoRetry: true, retryDelays: [0, 1000, 3000, 5000] // merge default options with the ones set by user };_this.opts = _extends({}, defaultOptions, opts); _this.uploaders = Object.create(null); _this.uploaderEvents = Object.create(null); _this.uploaderSockets = Object.create(null); _this.handleResetProgress = _this.handleResetProgress.bind(_this); _this.handleUpload = _this.handleUpload.bind(_this); return _this; } Tus.prototype.handleResetProgress = function handleResetProgress() { var files = _extends({}, this.uppy.state.files); Object.keys(files).forEach(function (fileID) { // Only clone the file object if it has a Tus `uploadUrl` attached. if (files[fileID].tus && files[fileID].tus.uploadUrl) { var tusState = _extends({}, files[fileID].tus); delete tusState.uploadUrl; files[fileID] = _extends({}, files[fileID], { tus: tusState }); } }); this.uppy.setState({ files: files }); }; /** * Clean up all references for a file's upload: the tus.Upload instance, * any events related to the file, and the uppy-server WebSocket connection. */ Tus.prototype.resetUploaderReferences = function resetUploaderReferences(fileID) { if (this.uploaders[fileID]) { this.uploaders[fileID].abort(); 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; } }; /** * Create a new Tus upload * * @param {object} file for use with upload * @param {integer} current file in a queue * @param {integer} total number of files in a queue * @returns {Promise} */ Tus.prototype.upload = function upload(file, current, total) { var _this2 = this; this.resetUploaderReferences(file.id); // Create a new tus upload return new _Promise(function (resolve, reject) { var optsTus = _extends({}, tusDefaultOptions, _this2.opts, // Install file-specific upload overrides. file.tus || {}); optsTus.onError = function (err) { _this2.uppy.log(err); _this2.uppy.emit('upload-error', file, err); err.message = 'Failed because: ' + err.message; _this2.resetUploaderReferences(file.id); reject(err); }; optsTus.onProgress = function (bytesUploaded, bytesTotal) { _this2.onReceiveUploadUrl(file, upload.url); _this2.uppy.emit('upload-progress', file, { uploader: _this2, bytesUploaded: bytesUploaded, bytesTotal: bytesTotal }); }; optsTus.onSuccess = function () { _this2.uppy.emit('upload-success', file, upload, upload.url); if (upload.url) { _this2.uppy.log('Download ' + upload.file.name + ' from ' + upload.url); } _this2.resetUploaderReferences(file.id); resolve(upload); }; optsTus.metadata = file.meta; var upload = new tus.Upload(file.data, optsTus); _this2.uploaders[file.id] = upload; _this2.uploaderEvents[file.id] = createEventTracker(_this2.uppy); _this2.onFileRemove(file.id, function (targetFileID) { _this2.resetUploaderReferences(file.id); resolve('upload ' + targetFileID + ' was removed'); }); _this2.onPause(file.id, function (isPaused) { if (isPaused) { upload.abort(); } else { upload.start(); } }); _this2.onPauseAll(file.id, function () { upload.abort(); }); _this2.onCancelAll(file.id, function () { _this2.resetUploaderReferences(file.id); }); _this2.onResumeAll(file.id, function () { if (file.error) { upload.abort(); } upload.start(); }); if (!file.isPaused) { upload.start(); } if (!file.isRestored) { _this2.uppy.emit('upload-started', file, upload); } }); }; Tus.prototype.uploadRemote = function uploadRemote(file, current, total) { var _this3 = this; this.resetUploaderReferences(file.id); var opts = _extends({}, this.opts, // Install file-specific upload overrides. file.tus || {}); return new _Promise(function (resolve, reject) { _this3.uppy.log(file.remote.url); if (file.serverToken) { return _this3.connectToServerSocket(file).then(function () { return resolve(); }).catch(reject); } _this3.uppy.emit('upload-started', file); fetch(file.remote.url, { method: 'post', credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(_extends({}, file.remote.body, { endpoint: opts.endpoint, uploadUrl: opts.uploadUrl, protocol: 'tus', size: file.data.size, metadata: file.meta })) }).then(function (res) { if (res.status < 200 || res.status > 300) { return reject(res.statusText); } return res.json().then(function (data) { _this3.uppy.setFileState(file.id, { serverToken: data.token }); file = _this3.getFile(file.id); return file; }); }).then(function (file) { return _this3.connectToServerSocket(file); }).then(function () { resolve(); }).catch(function (err) { reject(new Error(err)); }); }); }; Tus.prototype.connectToServerSocket = function connectToServerSocket(file) { var _this4 = this; return new _Promise(function (resolve, reject) { var token = file.serverToken; var host = getSocketHost(file.remote.host); var socket = new UppySocket({ target: host + '/api/' + token }); _this4.uploaderSockets[file.id] = socket; _this4.uploaderEvents[file.id] = createEventTracker(_this4.uppy); _this4.onFileRemove(file.id, function () { socket.send('pause', {}); resolve('upload ' + file.id + ' was removed'); }); _this4.onPause(file.id, function (isPaused) { isPaused ? socket.send('pause', {}) : socket.send('resume', {}); }); _this4.onPauseAll(file.id, function () { return socket.send('pause', {}); }); _this4.onCancelAll(file.id, function () { return socket.send('pause', {}); }); _this4.onResumeAll(file.id, function () { if (file.error) { socket.send('pause', {}); } socket.send('resume', {}); }); _this4.onRetry(file.id, function () { socket.send('pause', {}); socket.send('resume', {}); }); _this4.onRetryAll(file.id, function () { socket.send('pause', {}); socket.send('resume', {}); }); if (file.isPaused) { socket.send('pause', {}); } socket.on('progress', function (progressData) { return emitSocketProgress(_this4, progressData, file); }); socket.on('error', function (errData) { _this4.uppy.emit('upload-error', file, new Error(errData.error)); reject(new Error(errData.error)); }); socket.on('success', function (data) { _this4.uppy.emit('upload-success', file, data, data.url); _this4.resetUploaderReferences(file.id); resolve(); }); }); }; Tus.prototype.getFile = function getFile(fileID) { return this.uppy.state.files[fileID]; }; Tus.prototype.updateFile = function updateFile(file) { var _extends2; var files = _extends({}, this.uppy.state.files, (_extends2 = {}, _extends2[file.id] = file, _extends2)); this.uppy.setState({ files: files }); }; Tus.prototype.onReceiveUploadUrl = function onReceiveUploadUrl(file, uploadURL) { var currentFile = this.getFile(file.id); if (!currentFile) return; // Only do the update if we didn't have an upload URL yet, // or resume: false in options if ((!currentFile.tus || currentFile.tus.uploadUrl !== uploadURL) && this.opts.resume) { this.uppy.log('[Tus] Storing upload url'); var newFile = _extends({}, currentFile, { tus: _extends({}, currentFile.tus, { uploadUrl: uploadURL }) }); this.updateFile(newFile); } }; Tus.prototype.onFileRemove = function onFileRemove(fileID, cb) { this.uploaderEvents[fileID].on('file-removed', function (file) { if (fileID === file.id) cb(file.id); }); }; Tus.prototype.onPause = function onPause(fileID, cb) { this.uploaderEvents[fileID].on('upload-pause', function (targetFileID, isPaused) { if (fileID === targetFileID) { // const isPaused = this.uppy.pauseResume(fileID) cb(isPaused); } }); }; Tus.prototype.onRetry = function onRetry(fileID, cb) { this.uploaderEvents[fileID].on('upload-retry', function (targetFileID) { if (fileID === targetFileID) { cb(); } }); }; Tus.prototype.onRetryAll = function onRetryAll(fileID, cb) { var _this5 = this; this.uploaderEvents[fileID].on('retry-all', function (filesToRetry) { if (!_this5.uppy.getFile(fileID)) return; cb(); }); }; Tus.prototype.onPauseAll = function onPauseAll(fileID, cb) { var _this6 = this; this.uploaderEvents[fileID].on('pause-all', function () { if (!_this6.uppy.getFile(fileID)) return; cb(); }); }; Tus.prototype.onCancelAll = function onCancelAll(fileID, cb) { var _this7 = this; this.uploaderEvents[fileID].on('cancel-all', function () { if (!_this7.uppy.getFile(fileID)) return; cb(); }); }; Tus.prototype.onResumeAll = function onResumeAll(fileID, cb) { var _this8 = this; this.uploaderEvents[fileID].on('resume-all', function () { if (!_this8.uppy.getFile(fileID)) return; cb(); }); }; Tus.prototype.uploadFiles = function uploadFiles(files) { var _this9 = this; var promises = files.map(function (file, index) { var current = parseInt(index, 10) + 1; var total = files.length; if (file.error) { return _Promise.reject(new Error(file.error)); } _this9.uppy.log('uploading ' + current + ' of ' + total); if (file.isRemote) { return _this9.uploadRemote(file, current, total); } else { return _this9.upload(file, current, total); } }); return settle(promises); }; Tus.prototype.handleUpload = function handleUpload(fileIDs) { var _this10 = this; if (fileIDs.length === 0) { this.uppy.log('Tus: no files to upload!'); return _Promise.resolve(); } this.uppy.log('Tus is uploading...'); var filesToUpload = fileIDs.map(function (fileID) { return _this10.uppy.getFile(fileID); }); return this.uploadFiles(filesToUpload).then(function () { return null; }); }; Tus.prototype.addResumableUploadsCapabilityFlag = function addResumableUploadsCapabilityFlag() { var newCapabilities = _extends({}, this.uppy.getState().capabilities); newCapabilities.resumableUploads = true; this.uppy.setState({ capabilities: newCapabilities }); }; Tus.prototype.install = function install() { this.addResumableUploadsCapabilityFlag(); this.uppy.addUploader(this.handleUpload); this.uppy.on('reset-progress', this.handleResetProgress); if (this.opts.autoRetry) { this.uppy.on('back-online', this.uppy.retryAll); } }; Tus.prototype.uninstall = function uninstall() { this.uppy.removeUploader(this.handleUpload); if (this.opts.autoRetry) { this.uppy.off('back-online', this.uppy.retryAll); } }; return Tus; }(Plugin); //# sourceMappingURL=Tus.js.map