diff options
Diffstat (limited to 'apps/files/js/file-upload.js')
-rw-r--r-- | apps/files/js/file-upload.js | 1212 |
1 files changed, 0 insertions, 1212 deletions
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js deleted file mode 100644 index ad56492730e..00000000000 --- a/apps/files/js/file-upload.js +++ /dev/null @@ -1,1212 +0,0 @@ -/* - * Copyright (c) 2014 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ - -/** - * The file upload code uses several hooks to interact with blueimps jQuery file upload library: - * 1. the core upload handling hooks are added when initializing the plugin, - * 2. if the browser supports progress events they are added in a separate set after the initialization - * 3. every app can add it's own triggers for fileupload - * - files adds d'n'd handlers and also reacts to done events to add new rows to the filelist - * - TODO pictures upload button - * - TODO music upload button - */ - -/* global jQuery, humanFileSize, md5 */ - -/** - * File upload object - * - * @class OC.FileUpload - * @classdesc - * - * Represents a file upload - * - * @param {OC.Uploader} uploader uploader - * @param {Object} data blueimp data - */ -OC.FileUpload = function(uploader, data) { - this.uploader = uploader; - this.data = data; - var path = ''; - if (this.uploader.fileList) { - path = OC.joinPaths(this.uploader.fileList.getCurrentDirectory(), this.getFile().name); - } else { - path = this.getFile().name; - } - this.id = 'web-file-upload-' + md5(path) + '-' + (new Date()).getTime(); -}; -OC.FileUpload.CONFLICT_MODE_DETECT = 0; -OC.FileUpload.CONFLICT_MODE_OVERWRITE = 1; -OC.FileUpload.CONFLICT_MODE_AUTORENAME = 2; -OC.FileUpload.prototype = { - - /** - * Unique upload id - * - * @type string - */ - id: null, - - /** - * Upload element - * - * @type Object - */ - $uploadEl: null, - - /** - * Target folder - * - * @type string - */ - _targetFolder: '', - - /** - * @type int - */ - _conflictMode: OC.FileUpload.CONFLICT_MODE_DETECT, - - /** - * New name from server after autorename - * - * @type String - */ - _newName: null, - - /** - * Returns the unique upload id - * - * @return string - */ - getId: function() { - return this.id; - }, - - /** - * Returns the file to be uploaded - * - * @return {File} file - */ - getFile: function() { - return this.data.files[0]; - }, - - /** - * Return the final filename. - * - * @return {String} file name - */ - getFileName: function() { - // autorenamed name - if (this._newName) { - return this._newName; - } - return this.getFile().name; - }, - - setTargetFolder: function(targetFolder) { - this._targetFolder = targetFolder; - }, - - getTargetFolder: function() { - return this._targetFolder; - }, - - /** - * Get full path for the target file, including relative path, - * without the file name. - * - * @return {String} full path - */ - getFullPath: function() { - return OC.joinPaths(this._targetFolder, this.getFile().relativePath || ''); - }, - - /** - * Get full path for the target file, - * including relative path and file name. - * - * @return {String} full path - */ - getFullFilePath: function() { - return OC.joinPaths(this.getFullPath(), this.getFile().name); - }, - - /** - * Returns conflict resolution mode. - * - * @return {int} conflict mode - */ - getConflictMode: function() { - return this._conflictMode || OC.FileUpload.CONFLICT_MODE_DETECT; - }, - - /** - * Set conflict resolution mode. - * See CONFLICT_MODE_* constants. - * - * @param {int} mode conflict mode - */ - setConflictMode: function(mode) { - this._conflictMode = mode; - }, - - deleteUpload: function() { - delete this.data.jqXHR; - }, - - /** - * Trigger autorename and append "(2)". - * Multiple calls will increment the appended number. - */ - autoRename: function() { - var name = this.getFile().name; - if (!this._renameAttempt) { - this._renameAttempt = 1; - } - - var dotPos = name.lastIndexOf('.'); - var extPart = ''; - if (dotPos > 0) { - this._newName = name.substr(0, dotPos); - extPart = name.substr(dotPos); - } else { - this._newName = name; - } - - // generate new name - this._renameAttempt++; - this._newName = this._newName + ' (' + this._renameAttempt + ')' + extPart; - }, - - /** - * Submit the upload - */ - submit: function() { - var self = this; - var data = this.data; - var file = this.getFile(); - - // it was a folder upload, so make sure the parent directory exists alrady - var folderPromise; - if (file.relativePath) { - folderPromise = this.uploader.ensureFolderExists(this.getFullPath()); - } else { - folderPromise = $.Deferred().resolve().promise(); - } - - if (this.uploader.fileList) { - this.data.url = this.uploader.fileList.getUploadUrl(this.getFileName(), this.getFullPath()); - } - - if (!this.data.headers) { - this.data.headers = {}; - } - - // webdav without multipart - this.data.multipart = false; - this.data.type = 'PUT'; - - delete this.data.headers['If-None-Match']; - if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT - || this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { - this.data.headers['If-None-Match'] = '*'; - } - - var userName = this.uploader.davClient.getUserName(); - var password = this.uploader.davClient.getPassword(); - if (userName) { - // copy username/password from DAV client - this.data.headers['Authorization'] = - 'Basic ' + btoa(userName + ':' + (password || '')); - } - - var chunkFolderPromise; - if ($.support.blobSlice - && this.uploader.fileUploadParam.maxChunkSize - && this.getFile().size > this.uploader.fileUploadParam.maxChunkSize - ) { - data.isChunked = true; - chunkFolderPromise = this.uploader.davClient.createDirectory( - 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId() - ); - // TODO: if fails, it means same id already existed, need to retry - } else { - chunkFolderPromise = $.Deferred().resolve().promise(); - } - - // wait for creation of the required directory before uploading - $.when(folderPromise, chunkFolderPromise).then(function() { - data.submit(); - }, function() { - self.abort(); - }); - - }, - - /** - * Process end of transfer - */ - done: function() { - if (!this.data.isChunked) { - return $.Deferred().resolve().promise(); - } - - var uid = OC.getCurrentUser().uid; - var mtime = this.getFile().lastModified; - var size = this.getFile().size; - var headers = {}; - if (mtime) { - headers['X-OC-Mtime'] = mtime / 1000; - } - if (size) { - headers['OC-Total-Length'] = size; - - } - - return this.uploader.davClient.move( - 'uploads/' + uid + '/' + this.getId() + '/.file', - 'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()), - true, - headers - ); - }, - - _deleteChunkFolder: function() { - // delete transfer directory for this upload - this.uploader.davClient.remove( - 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId() - ); - }, - - /** - * Abort the upload - */ - abort: function() { - if (this.data.isChunked) { - this._deleteChunkFolder(); - } - this.data.abort(); - this.deleteUpload(); - }, - - /** - * Fail the upload - */ - fail: function() { - this.deleteUpload(); - if (this.data.isChunked) { - this._deleteChunkFolder(); - } - }, - - /** - * Returns the server response - * - * @return {Object} response - */ - getResponse: function() { - var response = this.data.response(); - if (response.errorThrown) { - // attempt parsing Sabre exception is available - var xml = response.jqXHR.responseXML; - if (xml.documentElement.localName === 'error' && xml.documentElement.namespaceURI === 'DAV:') { - var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message'); - var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception'); - if (messages.length) { - response.message = messages[0].textContent; - } - if (exceptions.length) { - response.exception = exceptions[0].textContent; - } - return response; - } - } - - if (typeof response.result !== 'string' && response.result) { - //fetch response from iframe - response = $.parseJSON(response.result[0].body.innerText); - if (!response) { - // likely due to internal server error - response = {status: 500}; - } - } else { - response = response.result; - } - return response; - }, - - /** - * Returns the status code from the response - * - * @return {int} status code - */ - getResponseStatus: function() { - if (this.uploader.isXHRUpload()) { - var xhr = this.data.response().jqXHR; - if (xhr) { - return xhr.status; - } - return null; - } - return this.getResponse().status; - }, - - /** - * Returns the response header by name - * - * @param {String} headerName header name - * @return {Array|String} response header value(s) - */ - getResponseHeader: function(headerName) { - headerName = headerName.toLowerCase(); - if (this.uploader.isXHRUpload()) { - return this.data.response().jqXHR.getResponseHeader(headerName); - } - - var headers = this.getResponse().headers; - if (!headers) { - return null; - } - - var value = _.find(headers, function(value, key) { - return key.toLowerCase() === headerName; - }); - if (_.isArray(value) && value.length === 1) { - return value[0]; - } - return value; - } -}; - -/** - * keeps track of uploads in progress and implements callbacks for the conflicts dialog - * @namespace - */ - -OC.Uploader = function() { - this.init.apply(this, arguments); -}; - -OC.Uploader.prototype = _.extend({ - /** - * @type Array<OC.FileUpload> - */ - _uploads: {}, - - /** - * List of directories known to exist. - * - * Key is the fullpath and value is boolean, true meaning that the directory - * was already created so no need to create it again. - */ - _knownDirs: {}, - - /** - * @type OCA.Files.FileList - */ - fileList: null, - - /** - * @type OC.Files.Client - */ - filesClient: null, - - /** - * Webdav client pointing at the root "dav" endpoint - * - * @type OC.Files.Client - */ - davClient: null, - - /** - * Function that will allow us to know if Ajax uploads are supported - * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html - * also see article @link http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata - */ - _supportAjaxUploadWithProgress: function() { - if (window.TESTING) { - return true; - } - return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData(); - - // Is the File API supported? - function supportFileAPI() { - var fi = document.createElement('INPUT'); - fi.type = 'file'; - return 'files' in fi; - } - - // Are progress events supported? - function supportAjaxUploadProgressEvents() { - var xhr = new XMLHttpRequest(); - return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)); - } - - // Is FormData supported? - function supportFormData() { - return !! window.FormData; - } - }, - - /** - * Returns whether an XHR upload will be used - * - * @return {bool} true if XHR upload will be used, - * false for iframe upload - */ - isXHRUpload: function () { - return !this.fileUploadParam.forceIframeTransport && - ((!this.fileUploadParam.multipart && $.support.xhrFileUpload) || - $.support.xhrFormDataFileUpload); - }, - - /** - * Makes sure that the upload folder and its parents exists - * - * @param {String} fullPath full path - * @return {Promise} promise that resolves when all parent folders - * were created - */ - ensureFolderExists: function(fullPath) { - if (!fullPath || fullPath === '/') { - return $.Deferred().resolve().promise(); - } - - // remove trailing slash - if (fullPath.charAt(fullPath.length - 1) === '/') { - fullPath = fullPath.substr(0, fullPath.length - 1); - } - - var self = this; - var promise = this._knownDirs[fullPath]; - - if (this.fileList) { - // assume the current folder exists - this._knownDirs[this.fileList.getCurrentDirectory()] = $.Deferred().resolve().promise(); - } - - if (!promise) { - var deferred = new $.Deferred(); - promise = deferred.promise(); - this._knownDirs[fullPath] = promise; - - // make sure all parents already exist - var parentPath = OC.dirname(fullPath); - var parentPromise = this._knownDirs[parentPath]; - if (!parentPromise) { - parentPromise = this.ensureFolderExists(parentPath); - } - - parentPromise.then(function() { - self.filesClient.createDirectory(fullPath).always(function(status) { - // 405 is expected if the folder already exists - if ((status >= 200 && status < 300) || status === 405) { - self.trigger('createdfolder', fullPath); - deferred.resolve(); - return; - } - OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: fullPath}), {type: 'error'}); - deferred.reject(); - }); - }, function() { - deferred.reject(); - }); - } - - return promise; - }, - - /** - * Submit the given uploads - * - * @param {Array} array of uploads to start - */ - submitUploads: function(uploads) { - var self = this; - _.each(uploads, function(upload) { - self._uploads[upload.data.uploadId] = upload; - upload.submit(); - }); - }, - - /** - * Show conflict for the given file object - * - * @param {OC.FileUpload} file upload object - */ - showConflict: function(fileUpload) { - //show "file already exists" dialog - var self = this; - var file = fileUpload.getFile(); - // already attempted autorename but the server said the file exists ? (concurrently added) - if (fileUpload.getConflictMode() === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { - // attempt another autorename, defer to let the current callback finish - _.defer(function() { - self.onAutorename(fileUpload); - }); - return; - } - // retrieve more info about this file - this.filesClient.getFileInfo(fileUpload.getFullFilePath()).then(function(status, fileInfo) { - var original = fileInfo; - var replacement = file; - original.directory = original.path; - OC.dialogs.fileexists(fileUpload, original, replacement, self); - }); - }, - /** - * cancels all uploads - */ - cancelUploads:function() { - this.log('canceling uploads'); - jQuery.each(this._uploads, function(i, upload) { - upload.abort(); - }); - this.clear(); - }, - /** - * Clear uploads - */ - clear: function() { - this._uploads = {}; - this._knownDirs = {}; - }, - /** - * Returns an upload by id - * - * @param {int} data uploadId - * @return {OC.FileUpload} file upload - */ - getUpload: function(data) { - if (_.isString(data)) { - return this._uploads[data]; - } else if (data.uploadId && this._uploads[data.uploadId]) { - this._uploads[data.uploadId].data = data; - return this._uploads[data.uploadId]; - } - return null; - }, - - showUploadCancelMessage: _.debounce(function() { - OC.Notification.show(t('files', 'Upload cancelled.'), {timeout : 7, type: 'error'}); - }, 500), - /** - * callback for the conflicts dialog - */ - onCancel:function() { - this.cancelUploads(); - }, - /** - * callback for the conflicts dialog - * calls onSkip, onReplace or onAutorename for each conflict - * @param {object} conflicts - list of conflict elements - */ - onContinue:function(conflicts) { - var self = this; - //iterate over all conflicts - jQuery.each(conflicts, function (i, conflict) { - conflict = $(conflict); - var keepOriginal = conflict.find('.original input[type="checkbox"]:checked').length === 1; - var keepReplacement = conflict.find('.replacement input[type="checkbox"]:checked').length === 1; - if (keepOriginal && keepReplacement) { - // when both selected -> autorename - self.onAutorename(conflict.data('data')); - } else if (keepReplacement) { - // when only replacement selected -> overwrite - self.onReplace(conflict.data('data')); - } else { - // when only original seleted -> skip - // when none selected -> skip - self.onSkip(conflict.data('data')); - } - }); - }, - /** - * handle skipping an upload - * @param {OC.FileUpload} upload - */ - onSkip:function(upload) { - this.log('skip', null, upload); - upload.deleteUpload(); - }, - /** - * handle replacing a file on the server with an uploaded file - * @param {FileUpload} data - */ - onReplace:function(upload) { - this.log('replace', null, upload); - upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_OVERWRITE); - this.submitUploads([upload]); - }, - /** - * handle uploading a file and letting the server decide a new name - * @param {object} upload - */ - onAutorename:function(upload) { - this.log('autorename', null, upload); - upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_AUTORENAME); - - do { - upload.autoRename(); - // if file known to exist on the client side, retry - } while (this.fileList && this.fileList.inList(upload.getFileName())); - - // resubmit upload - this.submitUploads([upload]); - }, - _trace:false, //TODO implement log handler for JS per class? - log:function(caption, e, data) { - if (this._trace) { - console.log(caption); - console.log(data); - } - }, - /** - * checks the list of existing files prior to uploading and shows a simple dialog to choose - * skip all, replace all or choose which files to keep - * - * @param {array} selection of files to upload - * @param {object} callbacks - object with several callback methods - * @param {function} callbacks.onNoConflicts - * @param {function} callbacks.onSkipConflicts - * @param {function} callbacks.onReplaceConflicts - * @param {function} callbacks.onChooseConflicts - * @param {function} callbacks.onCancel - */ - checkExistingFiles: function (selection, callbacks) { - var fileList = this.fileList; - var conflicts = []; - // only keep non-conflicting uploads - selection.uploads = _.filter(selection.uploads, function(upload) { - var file = upload.getFile(); - if (file.relativePath) { - // can't check in subfolder contents - return true; - } - if (!fileList) { - // no list to check against - return true; - } - var fileInfo = fileList.findFile(file.name); - if (fileInfo) { - conflicts.push([ - // original - _.extend(fileInfo, { - directory: fileInfo.directory || fileInfo.path || fileList.getCurrentDirectory() - }), - // replacement (File object) - upload - ]); - return false; - } - return true; - }); - if (conflicts.length) { - // wait for template loading - OC.dialogs.fileexists(null, null, null, this).done(function() { - _.each(conflicts, function(conflictData) { - OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].getFile(), this); - }); - }); - } - - // upload non-conflicting files - // note: when reaching the server they might still meet conflicts - // if the folder was concurrently modified, these will get added - // to the already visible dialog, if applicable - callbacks.onNoConflicts(selection); - }, - - _hideProgressBar: function() { - var self = this; - $('#uploadprogresswrapper .stop').fadeOut(); - $('#uploadprogressbar').fadeOut(function() { - self.$uploadEl.trigger(new $.Event('resized')); - }); - }, - - _showProgressBar: function() { - $('#uploadprogressbar').fadeIn(); - this.$uploadEl.trigger(new $.Event('resized')); - }, - - /** - * Returns whether the given file is known to be a received shared file - * - * @param {Object} file file - * @return {bool} true if the file is a shared file - */ - _isReceivedSharedFile: function(file) { - if (!window.FileList) { - return false; - } - var $tr = window.FileList.findFileEl(file.name); - if (!$tr.length) { - return false; - } - - return ($tr.attr('data-mounttype') === 'shared-root' && $tr.attr('data-mime') !== 'httpd/unix-directory'); - }, - - /** - * Initialize the upload object - * - * @param {Object} $uploadEl upload element - * @param {Object} options - * @param {OCA.Files.FileList} [options.fileList] file list object - * @param {OC.Files.Client} [options.filesClient] files client object - * @param {Object} [options.dropZone] drop zone for drag and drop upload - */ - init: function($uploadEl, options) { - var self = this; - - options = options || {}; - - this.fileList = options.fileList; - this.filesClient = options.filesClient || OC.Files.getClient(); - this.davClient = new OC.Files.Client({ - host: this.filesClient.getHost(), - root: OC.linkToRemoteBase('dav'), - useHTTPS: OC.getProtocol() === 'https', - userName: this.filesClient.getUserName(), - password: this.filesClient.getPassword() - }); - - $uploadEl = $($uploadEl); - this.$uploadEl = $uploadEl; - - if ($uploadEl.exists()) { - $('#uploadprogresswrapper .stop').on('click', function() { - self.cancelUploads(); - }); - - this.fileUploadParam = { - type: 'PUT', - dropZone: options.dropZone, // restrict dropZone to content div - autoUpload: false, - sequentialUploads: true, - //singleFileUploads is on by default, so the data.files array will always have length 1 - /** - * on first add of every selection - * - check all files of originalFiles array with files in dir - * - on conflict show dialog - * - skip all -> remember as single skip action for all conflicting files - * - replace all -> remember as single replace action for all conflicting files - * - choose -> show choose dialog - * - mark files to keep - * - when only existing -> remember as single skip action - * - when only new -> remember as single replace action - * - when both -> remember as single autorename action - * - start uploading selection - * @param {object} e - * @param {object} data - * @returns {boolean} - */ - add: function(e, data) { - self.log('add', e, data); - var that = $(this), freeSpace; - - var upload = new OC.FileUpload(self, data); - // can't link directly due to jQuery not liking cyclic deps on its ajax object - data.uploadId = upload.getId(); - - // we need to collect all data upload objects before - // starting the upload so we can check their existence - // and set individual conflict actions. Unfortunately, - // there is only one variable that we can use to identify - // the selection a data upload is part of, so we have to - // collect them in data.originalFiles turning - // singleFileUploads off is not an option because we want - // to gracefully handle server errors like 'already exists' - - // create a container where we can store the data objects - if ( ! data.originalFiles.selection ) { - // initialize selection and remember number of files to upload - data.originalFiles.selection = { - uploads: [], - filesToUpload: data.originalFiles.length, - totalBytes: 0 - }; - } - // TODO: move originalFiles to a separate container, maybe inside OC.Upload - var selection = data.originalFiles.selection; - - // add uploads - if ( selection.uploads.length < selection.filesToUpload ) { - // remember upload - selection.uploads.push(upload); - } - - //examine file - var file = upload.getFile(); - try { - // FIXME: not so elegant... need to refactor that method to return a value - Files.isFileNameValid(file.name); - } - catch (errorMessage) { - data.textStatus = 'invalidcharacters'; - data.errorThrown = errorMessage; - } - - if (data.targetDir) { - upload.setTargetFolder(data.targetDir); - delete data.targetDir; - } - - // in case folder drag and drop is not supported file will point to a directory - // http://stackoverflow.com/a/20448357 - if ( ! file.type && file.size % 4096 === 0 && file.size <= 102400) { - var dirUploadFailure = false; - try { - var reader = new FileReader(); - reader.readAsBinaryString(file); - } catch (NS_ERROR_FILE_ACCESS_DENIED) { - //file is a directory - dirUploadFailure = true; - } - - if (dirUploadFailure) { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files', - 'Unable to upload {filename} as it is a directory or has 0 bytes', - {filename: file.name} - ); - } - } - - // only count if we're not overwriting an existing shared file - if (self._isReceivedSharedFile(file)) { - file.isReceivedShare = true; - } else { - // add size - selection.totalBytes += file.size; - } - - // check free space - freeSpace = $('#free_space').val(); - if (freeSpace >= 0 && selection.totalBytes > freeSpace) { - data.textStatus = 'notenoughspace'; - data.errorThrown = t('files', - 'Not enough free space, you are uploading {size1} but only {size2} is left', { - 'size1': humanFileSize(selection.totalBytes), - 'size2': humanFileSize($('#free_space').val()) - }); - } - - // end upload for whole selection on error - if (data.errorThrown) { - // trigger fileupload fail handler - var fu = that.data('blueimp-fileupload') || that.data('fileupload'); - fu._trigger('fail', e, data); - return false; //don't upload anything - } - - // check existing files when all is collected - if ( selection.uploads.length >= selection.filesToUpload ) { - - //remove our selection hack: - delete data.originalFiles.selection; - - var callbacks = { - - onNoConflicts: function (selection) { - self.submitUploads(selection.uploads); - }, - onSkipConflicts: function (selection) { - //TODO mark conflicting files as toskip - }, - onReplaceConflicts: function (selection) { - //TODO mark conflicting files as toreplace - }, - onChooseConflicts: function (selection) { - //TODO mark conflicting files as chosen - }, - onCancel: function (selection) { - $.each(selection.uploads, function(i, upload) { - upload.abort(); - }); - } - }; - - self.checkExistingFiles(selection, callbacks); - - } - - return true; // continue adding files - }, - /** - * called after the first add, does NOT have the data param - * @param {object} e - */ - start: function(e) { - self.log('start', e, null); - //hide the tooltip otherwise it covers the progress bar - $('#upload').tooltip('hide'); - }, - fail: function(e, data) { - var upload = self.getUpload(data); - var status = null; - if (upload) { - status = upload.getResponseStatus(); - } - self.log('fail', e, upload); - self._hideProgressBar(); - - if (data.textStatus === 'abort') { - self.showUploadCancelMessage(); - } else if (status === 412) { - // file already exists - self.showConflict(upload); - } else if (status === 404) { - // target folder does not exist any more - OC.Notification.show(t('files', 'Target folder "{dir}" does not exist any more', {dir: upload.getFullPath()} ), {type: 'error'}); - self.cancelUploads(); - } else if (status === 507) { - // not enough space - OC.Notification.show(t('files', 'Not enough free space'), {type: 'error'}); - self.cancelUploads(); - } else { - // HTTP connection problem or other error - var message = ''; - if (upload) { - var response = upload.getResponse(); - message = response.message; - } - OC.Notification.show(message || data.errorThrown, {type: 'error'}); - } - - if (upload) { - upload.fail(); - } - }, - /** - * called for every successful upload - * @param {object} e - * @param {object} data - */ - done:function(e, data) { - var upload = self.getUpload(data); - var that = $(this); - self.log('done', e, upload); - - var status = upload.getResponseStatus(); - if (status < 200 || status >= 300) { - // trigger fail handler - var fu = that.data('blueimp-fileupload') || that.data('fileupload'); - fu._trigger('fail', e, data); - return; - } - }, - /** - * called after last upload - * @param {object} e - * @param {object} data - */ - stop: function(e, data) { - self.log('stop', e, data); - } - }; - - if (options.maxChunkSize) { - this.fileUploadParam.maxChunkSize = options.maxChunkSize; - } - - // initialize jquery fileupload (blueimp) - var fileupload = this.$uploadEl.fileupload(this.fileUploadParam); - - if (this._supportAjaxUploadWithProgress()) { - //remaining time - var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferIndex2, bufferTotal; - - // add progress handlers - fileupload.on('fileuploadadd', function(e, data) { - self.log('progress handle fileuploadadd', e, data); - self.trigger('add', e, data); - }); - // add progress handlers - fileupload.on('fileuploadstart', function(e, data) { - self.log('progress handle fileuploadstart', e, data); - $('#uploadprogresswrapper .stop').show(); - $('#uploadprogresswrapper .label').show(); - $('#uploadprogressbar').progressbar({value: 0}); - $('#uploadprogressbar .ui-progressbar-value'). - html('<em class="label inner"><span class="desktop">' - + t('files', 'Uploading …') - + '</span><span class="mobile">' - + t('files', '…') - + '</span></em>'); - $('#uploadprogressbar').tooltip({placement: 'bottom'}); - self._showProgressBar(); - // initial remaining time variables - lastUpdate = new Date().getTime(); - lastSize = 0; - bufferSize = 20; - buffer = []; - bufferIndex = 0; - bufferIndex2 = 0; - bufferTotal = 0; - for(var i = 0; i < bufferSize; i++){ - buffer[i] = 0; - } - self.trigger('start', e, data); - }); - fileupload.on('fileuploadprogress', function(e, data) { - self.log('progress handle fileuploadprogress', e, data); - //TODO progressbar in row - self.trigger('progress', e, data); - }); - fileupload.on('fileuploadprogressall', function(e, data) { - self.log('progress handle fileuploadprogressall', e, data); - var progress = (data.loaded / data.total) * 100; - var thisUpdate = new Date().getTime(); - var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s - lastUpdate = thisUpdate; - var diffSize = data.loaded - lastSize; - lastSize = data.loaded; - diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1MiB/2s = 0.5MiB/s, unit is byte per second - var remainingSeconds = ((data.total - data.loaded) / diffSize); - if(remainingSeconds >= 0) { - bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds; - buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother - bufferIndex = (bufferIndex + 1) % bufferSize; - bufferIndex2++; - } - var smoothRemainingSeconds; - if (bufferIndex2 > 0 && bufferIndex2 < 20) { - smoothRemainingSeconds = bufferTotal / bufferIndex2; - } else if (bufferSize > 0) { - smoothRemainingSeconds = bufferTotal / bufferSize; - } else { - smoothRemainingSeconds = 1; - } - - var h = moment.duration(smoothRemainingSeconds, "seconds").humanize(); - if (!(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) { - // show "Uploading ..." for durations longer than 4 hours - h = t('files', 'Uploading …'); - } - $('#uploadprogressbar .label .mobile').text(h); - $('#uploadprogressbar .label .desktop').text(h); - $('#uploadprogressbar').attr('original-title', - t('files', '{loadedSize} of {totalSize} ({bitrate})' , { - loadedSize: humanFileSize(data.loaded), - totalSize: humanFileSize(data.total), - bitrate: humanFileSize(data.bitrate / 8) + '/s' - }) - ); - $('#uploadprogressbar').progressbar('value', progress); - self.trigger('progressall', e, data); - }); - fileupload.on('fileuploadstop', function(e, data) { - self.log('progress handle fileuploadstop', e, data); - - self.clear(); - self.trigger('stop', e, data); - }); - fileupload.on('fileuploadfail', function(e, data) { - self.log('progress handle fileuploadfail', e, data); - //if user pressed cancel hide upload progress bar and cancel button - if (data.errorThrown === 'abort') { - self._hideProgressBar(); - } - self.trigger('fail', e, data); - }); - var disableDropState = function() { - $('#app-content').removeClass('file-drag'); - $('.dropping-to-dir').removeClass('dropping-to-dir'); - $('.dir-drop').removeClass('dir-drop'); - $('.icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept'); - }; - var disableClassOnFirefox = _.debounce(function() { - disableDropState(); - }, 100); - fileupload.on('fileuploaddragover', function(e){ - $('#app-content').addClass('file-drag'); - // dropping a folder in firefox doesn't cause a drop event - // this is simulated by simply invoke disabling all classes - // once no dragover event isn't noticed anymore - if (/Firefox/i.test(navigator.userAgent)) { - disableClassOnFirefox(); - } - $('#emptycontent .icon-folder').addClass('icon-filetype-folder-drag-accept'); - - var filerow = $(e.delegatedEvent.target).closest('tr'); - - if(!filerow.hasClass('dropping-to-dir')){ - $('.dropping-to-dir .icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept'); - $('.dropping-to-dir').removeClass('dropping-to-dir'); - $('.dir-drop').removeClass('dir-drop'); - } - - if(filerow.attr('data-type') === 'dir'){ - $('#app-content').addClass('dir-drop'); - filerow.addClass('dropping-to-dir'); - filerow.find('.thumbnail').addClass('icon-filetype-folder-drag-accept'); - } - }); - fileupload.on('fileuploaddragleave fileuploaddrop', function (){ - $('#app-content').removeClass('file-drag'); - $('.dropping-to-dir').removeClass('dropping-to-dir'); - $('.dir-drop').removeClass('dir-drop'); - $('.icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept'); - }); - - fileupload.on('fileuploadchunksend', function(e, data) { - // modify the request to adjust it to our own chunking - var upload = self.getUpload(data); - var range = data.contentRange.split(' ')[1]; - var chunkId = range.split('/')[0].split('-')[0]; - data.url = OC.getRootPath() + - '/remote.php/dav/uploads' + - '/' + OC.getCurrentUser().uid + - '/' + upload.getId() + - '/' + chunkId; - delete data.contentRange; - delete data.headers['Content-Range']; - }); - fileupload.on('fileuploaddone', function(e, data) { - var upload = self.getUpload(data); - upload.done().then(function() { - self._hideProgressBar(); - self.trigger('done', e, upload); - }).fail(function(status, response) { - var message = response.message; - self._hideProgressBar(); - if (status === 507) { - // not enough space - OC.Notification.show(message || t('files', 'Not enough free space'), {type: 'error'}); - self.cancelUploads(); - } else if (status === 409) { - OC.Notification.show(message || t('files', 'Target folder does not exist any more'), {type: 'error'}); - } else { - OC.Notification.show(message || t('files', 'Error when assembling chunks, status code {status}', {status: status}), {type: 'error'}); - } - self.trigger('fail', e, data); - }); - }); - fileupload.on('fileuploaddrop', function(e, data) { - self.trigger('drop', e, data); - if (e.isPropagationStopped()) { - return false; - } - }); - - } - } - - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) - if (navigator.userAgent.search(/konqueror/i) === -1) { - this.$uploadEl.attr('multiple', 'multiple'); - } - - return this.fileUploadParam; - } -}, OC.Backbone.Events); |