From f94e6036980644bdd6312e75a8973f2633cf5ff2 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 16 Aug 2013 11:40:55 +0200 Subject: progress fixes --- apps/files/js/files.js | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 3fad3fae7d3..a907aeab1fc 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,31 +1,5 @@ var uploadingFiles = {}; Files={ - cancelUpload:function(filename) { - if(uploadingFiles[filename]) { - uploadingFiles[filename].abort(); - delete uploadingFiles[filename]; - return true; - } - return false; - }, - cancelUploads:function() { - $.each(uploadingFiles,function(index,file) { - if(typeof file['abort'] === 'function') { - file.abort(); - var filename = $('tr').filterAttr('data-file',index); - filename.hide(); - filename.find('input[type="checkbox"]').removeAttr('checked'); - filename.removeClass('selected'); - } else { - $.each(file,function(i,f) { - f.abort(); - delete file[i]; - }); - } - delete uploadingFiles[index]; - }); - procesSelection(); - }, updateMaxUploadFilesize:function(response) { if(response == undefined) { return; @@ -117,7 +91,8 @@ $(document).ready(function() { // Trigger cancelling of file upload $('#uploadprogresswrapper .stop').on('click', function() { - Files.cancelUploads(); + OC.Upload.cancelUploads(); + procesSelection(); }); // Show trash bin -- cgit v1.2.3 From bf04daff82758fe9913c706eef07aed30c9b35ea Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 21 Aug 2013 14:58:28 +0200 Subject: architecture too complex --- 3rdparty | 2 +- apps/files/js/file-upload.js | 109 +++- apps/files/js/filelist.js | 51 +- apps/files/js/files.js | 7 - apps/files/js/jquery.fileupload.js | 1023 ++++++++++++++++++++++-------- apps/files/js/jquery.iframe-transport.js | 70 +- apps/files/templates/part.list.php | 11 +- 7 files changed, 934 insertions(+), 339 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/3rdparty b/3rdparty index 2f3ae9f56a9..75a05d76ab8 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 2f3ae9f56a9838b45254393e13c14f8a8c380d6b +Subproject commit 75a05d76ab86ba7454b4312fd0ff2ca5bd5828cf diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f8899cb07e6..c620942170c 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,7 @@ /** + * + * use put t ocacnel upload before it starts? use chunked uploads? + * * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object * - every new selection increases the total size and number of files for a directory @@ -63,6 +66,7 @@ OC.Upload = { * @type Array */ _selections: {}, + _selectionCount: 0, /* * queue which progress tracker to use for the next upload * @type Array @@ -77,7 +81,7 @@ OC.Upload = { }, getSelection:function(originalFiles) { if (!originalFiles.selectionKey) { - originalFiles.selectionKey = 'selection-' + $.assocArraySize(this._selections); + originalFiles.selectionKey = 'selection-' + this._selectionCount++; this._selections[originalFiles.selectionKey] = { selectionKey:originalFiles.selectionKey, files:{}, @@ -90,22 +94,41 @@ OC.Upload = { } return this._selections[originalFiles.selectionKey]; }, + deleteSelection:function(selectionKey) { + if (this._selections[selectionKey]) { + jQuery.each(this._selections[selectionKey].uploads, function(i, upload) { + upload.abort(); + }); + delete this._selections[selectionKey]; + } else { + console.log('OC.Upload: selection ' + selectionKey + ' does not exist'); + } + }, + deleteSelectionUpload:function(selection, filename) { + if(selection.uploads[filename]) { + selection.uploads[filename].abort(); + return true; + } else { + console.log('OC.Upload: selection ' + selection.selectionKey + ' does not contain upload for ' + filename); + } + return false; + }, cancelUpload:function(dir, filename) { + var self = this; var deleted = false; jQuery.each(this._selections, function(i, selection) { if (selection.dir === dir && selection.uploads[filename]) { - delete selection.uploads[filename]; - deleted = true; + deleted = self.deleteSelectionUpload(selection, filename); return false; // end searching through selections } }); return deleted; }, cancelUploads:function() { - jQuery.each(this._selections,function(i,selection){ - jQuery.each(selection.uploads, function (j, jqXHR) { - delete jqXHR; - }); + console.log('canceling uploads'); + var self = this; + jQuery.each(this._selections,function(i, selection){ + self.deleteSelection(selection.selectionKey); }); this._queue = []; this._isProcessing = false; @@ -132,7 +155,7 @@ OC.Upload = { } else { //queue is empty, we are done this._isProcessing = false; - //TODO free data + OC.Upload.cancelUploads(); } }, progressBytes: function() { @@ -157,13 +180,13 @@ OC.Upload = { total += selection.totalBytes; }); return total; - }, - handleExists:function(data) { - }, onCancel:function(data){ - //TODO cancel all uploads - OC.Upload.cancelUploads(); + //TODO cancel all uploads of this selection + + var selection = this.getSelection(data.originalFiles); + OC.Upload.deleteSelection(selection.selectionKey); + //FIXME hide progressbar }, onSkip:function(data){ var selection = this.getSelection(data.originalFiles); @@ -171,20 +194,19 @@ OC.Upload = { this.nextUpload(); }, onReplace:function(data){ - //TODO overwrite file data.data.append('replace', true); data.submit(); }, onRename:function(data, newName){ - //TODO rename file in filelist, stop spinner data.data.append('newname', newName); data.submit(); }, - setAction:function(data, action) { - - }, - setDefaultAction:function(action) { - + logStatus:function(caption, e, data) { + console.log(caption+' ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + if (data) { + console.log(data); + } + console.log(e); } }; @@ -195,6 +217,7 @@ $(document).ready(function() { //singleFileUploads is on by default, so the data.files array will always have length 1 add: function(e, data) { + OC.Upload.logStatus('add', e, data); var that = $(this); // lookup selection for dir @@ -267,14 +290,17 @@ $(document).ready(function() { * @param e */ start: function(e) { + OC.Upload.logStatus('start', e, null); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return true; } + $('#uploadprogresswrapper input.stop').show(); $('#uploadprogressbar').progressbar({value:0}); $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { + OC.Upload.logStatus('fail', e, data); if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { if (data.textStatus === 'abort') { $('#notification').text(t('files', 'Upload cancelled.')); @@ -289,12 +315,26 @@ $(document).ready(function() { }, 5000); } var selection = OC.Upload.getSelection(data.originalFiles); - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, progress: function(e, data) { + OC.Upload.logStatus('progress', e, data); // TODO: show nice progress bar in file row }, + /** + * + * @param {type} e + * @param {type} data (only has loaded, total and lengthComputable) + * @returns {unresolved} + */ progressall: function(e, data) { + OC.Upload.logStatus('progressall', e, data); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return; @@ -309,6 +349,7 @@ $(document).ready(function() { * @param data */ done:function(e, data) { + OC.Upload.logStatus('done', e, data); // handle different responses (json or body from iframe for ie) var response; if (typeof data.result === 'string') { @@ -323,7 +364,9 @@ $(document).ready(function() { if(typeof result[0] !== 'undefined' && result[0].status === 'success' ) { - selection.loadedBytes+=data.loaded; + if (selection) { + selection.loadedBytes+=data.loaded; + } OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { @@ -333,13 +376,19 @@ $(document).ready(function() { var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); fu._trigger('fail', e, data); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, /** @@ -348,7 +397,10 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - if(OC.Upload.progressBytes()>=100) { + OC.Upload.logStatus('stop', e, data); + if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + OC.Upload.cancelUploads(); //cleanup if(data.dataType !== 'iframe') { $('#uploadprogresswrapper input.stop').hide(); @@ -362,6 +414,11 @@ $(document).ready(function() { $('#uploadprogressbar').progressbar('value', 100); $('#uploadprogressbar').fadeOut(); } + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } } }; @@ -384,8 +441,8 @@ $(document).ready(function() { }; // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) { + $(window).on('beforeunload', function(e) { + if (OC.Upload.isProcessing()) { return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); } }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 335f81e04b9..eb57672e464 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -409,7 +409,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - console.log('fileuploaddrop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -448,7 +448,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - console.log('fileuploadadd ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -482,8 +482,11 @@ $(document).ready(function(){ } }); + file_upload_start.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('fileuploadstart', e, data); + }); file_upload_start.on('fileuploaddone', function(e, data) { - console.log('fileuploaddone ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -545,28 +548,58 @@ $(document).ready(function(){ }); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadalways', function(e, data) { - console.log('fileuploadalways ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - console.log('fileuploadsend ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - console.log('fileuploadprogress ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - console.log('fileuploadprogressall ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - console.log('fileuploadstop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadstop', e, data); + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadfail', function(e, data) { - console.log('fileuploadfail ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadfail', e, data); + + //if user pressed cancel hide upload chrome + if (data.errorThrown === 'abort') { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); /* file_upload_start.on('fileuploadfail', function(e, data) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index a907aeab1fc..53405c7fe7e 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,4 +1,3 @@ -var uploadingFiles = {}; Files={ updateMaxUploadFilesize:function(response) { if(response == undefined) { @@ -235,12 +234,6 @@ $(document).ready(function() { return size; }; - // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) - return t('files','File upload is in progress. Leaving the page now will cancel the upload.'); - }); - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) if(navigator.userAgent.search(/konqueror/i)==-1){ $('#file_upload_start').attr('multiple','multiple') diff --git a/apps/files/js/jquery.fileupload.js b/apps/files/js/jquery.fileupload.js index a89e9dc2c44..f9f6cc3a382 100644 --- a/apps/files/js/jquery.fileupload.js +++ b/apps/files/js/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.9 + * jQuery File Upload Plugin 5.32.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, Blob, FormData, location */ +/*global define, window, document, location, File, Blob, FormData */ (function (factory) { 'use strict'; @@ -27,12 +27,28 @@ }(function ($) { 'use strict'; + // Detect file input support, based on + // http://viljamis.com/blog/2012/file-upload-support-on-mobile/ + $.support.fileInput = !(new RegExp( + // Handle devices which give false positives for the feature detection: + '(Android (1\\.[0156]|2\\.[01]))' + + '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' + + '|(w(eb)?OSBrowser)|(webOS)' + + '|(Kindle/(1\\.0|2\\.[05]|3\\.0))' + ).test(window.navigator.userAgent) || + // Feature detection for all other devices: + $('').prop('disabled')); + // The FileReader API is not actually used, but works as feature detection, // as e.g. Safari supports XHR file uploads via the FormData API, // but not non-multipart XHR file uploads: $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; + // Detect support for Blob slicing (required for chunked uploads): + $.support.blobSlice = window.Blob && (Blob.prototype.slice || + Blob.prototype.webkitSlice || Blob.prototype.mozSlice); + // The fileupload widget listens for change events on file input fields defined // via fileInput setting and paste or drop events of the given dropZone. // In addition to the default jQuery Widget methods, the fileupload widget @@ -44,17 +60,16 @@ $.widget('blueimp.fileupload', { options: { - // The namespace used for event handler binding on the dropZone and - // fileInput collections. - // If not set, the name of the widget ("fileupload") is used. - namespace: undefined, - // The drop target collection, by the default the complete document. - // Set to null or an empty collection to disable drag & drop support: + // The drop target element(s), by the default the complete document. + // Set to null to disable drag & drop support: dropZone: $(document), - // The file input field collection, that is listened for change events. + // The paste target element(s), by the default the complete document. + // Set to null to disable paste support: + pasteZone: $(document), + // The file input field(s), that are listened to for change events. // If undefined, it is set to the file input fields inside // of the widget element on plugin initialization. - // Set to null or an empty collection to disable the change listener. + // Set to null to disable the change listener. fileInput: undefined, // By default, the file input field is replaced with a clone after // each input field change event. This is required for iframe transport @@ -63,7 +78,8 @@ replaceFileInput: true, // The parameter name for the file form data (the request argument name). // If undefined or empty, the name property of the file input field is - // used, or "files[]" if the file input name property is also empty: + // used, or "files[]" if the file input name property is also empty, + // can be a string or an array of strings: paramName: undefined, // By default, each file of a selection is uploaded using an individual // request for XHR type uploads. Set to false to upload file @@ -108,6 +124,29 @@ // global progress calculation. Set the following option to false to // prevent recalculating the global progress data: recalculateProgress: true, + // Interval in milliseconds to calculate and trigger progress events: + progressInterval: 100, + // Interval in milliseconds to calculate progress bitrate: + bitrateInterval: 500, + // By default, uploads are started automatically when adding files: + autoUpload: true, + + // Error and info messages: + messages: { + uploadedBytes: 'Uploaded bytes exceed file size' + }, + + // Translation function, gets the message key to be translated + // and an object with context specific data as arguments: + i18n: function (message, context) { + message = this.messages[message] || message.toString(); + if (context) { + $.each(context, function (key, value) { + message = message.replace('{' + key + '}', value); + }); + } + return message; + }, // Additional form data to be sent along with the file uploads can be set // using this option, which accepts an array of objects with name and @@ -121,48 +160,81 @@ // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop, paste or add API call). // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uplaods, else + // called once for each file in the selection for XHR file uploads, else // once for each file selection. + // // The upload starts when the submit method is invoked on the data parameter. // The data object contains a files property holding the added files - // and allows to override plugin options as well as define ajax settings. + // and allows you to override plugin options as well as define ajax settings. + // // Listeners for this callback can also be bound the following way: // .bind('fileuploadadd', func); + // // data.submit() returns a Promise object and allows to attach additional // handlers using jQuery's Deferred callbacks: // data.submit().done(func).fail(func).always(func); add: function (e, data) { - data.submit(); + if (data.autoUpload || (data.autoUpload !== false && + $(this).fileupload('option', 'autoUpload'))) { + data.process().done(function () { + data.submit(); + }); + } }, // Other callbacks: + // Callback for the submit event of each file upload: // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); + // Callback for the start of each file upload request: // send: function (e, data) {}, // .bind('fileuploadsend', func); + // Callback for successful uploads: // done: function (e, data) {}, // .bind('fileuploaddone', func); + // Callback for failed (abort or error) uploads: // fail: function (e, data) {}, // .bind('fileuploadfail', func); + // Callback for completed (success, abort or error) requests: // always: function (e, data) {}, // .bind('fileuploadalways', func); + // Callback for upload progress events: // progress: function (e, data) {}, // .bind('fileuploadprogress', func); + // Callback for global upload progress events: // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); + // Callback for uploads start, equivalent to the global ajaxStart event: // start: function (e) {}, // .bind('fileuploadstart', func); + // Callback for uploads stop, equivalent to the global ajaxStop event: // stop: function (e) {}, // .bind('fileuploadstop', func); - // Callback for change events of the fileInput collection: + + // Callback for change events of the fileInput(s): // change: function (e, data) {}, // .bind('fileuploadchange', func); - // Callback for paste events to the dropZone collection: + + // Callback for paste events to the pasteZone(s): // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - // Callback for drop events of the dropZone collection: + + // Callback for drop events of the dropZone(s): // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - // Callback for dragover events of the dropZone collection: + + // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); + // Callback for the start of each chunk upload request: + // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); + + // Callback for successful chunk uploads: + // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func); + + // Callback for failed (abort or error) chunk uploads: + // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func); + + // Callback for completed (success, abort or error) chunk upload requests: + // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func); + // The plugin options are used as settings object for the ajax calls. // The following are jQuery ajax settings required for the file uploads: processData: false, @@ -170,15 +242,36 @@ cache: false }, - // A list of options that require a refresh after assigning a new value: - _refreshOptionsList: [ - 'namespace', - 'dropZone', + // A list of options that require reinitializing event listeners and/or + // special initialization code: + _specialOptions: [ 'fileInput', + 'dropZone', + 'pasteZone', 'multipart', 'forceIframeTransport' ], + _blobSlice: $.support.blobSlice && function () { + var slice = this.slice || this.webkitSlice || this.mozSlice; + return slice.apply(this, arguments); + }, + + _BitrateTimer: function () { + this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); + this.loaded = 0; + this.bitrate = 0; + this.getBitrate = function (now, loaded, interval) { + var timeDiff = now - this.timestamp; + if (!this.bitrate || !interval || timeDiff > interval) { + this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8; + this.loaded = loaded; + this.timestamp = now; + } + return this.bitrate; + }; + }, + _isXHRUpload: function (options) { return !options.forceIframeTransport && ((!options.multipart && $.support.xhrFileUpload) || @@ -189,9 +282,11 @@ var formData; if (typeof options.formData === 'function') { return options.formData(options.form); - } else if ($.isArray(options.formData)) { + } + if ($.isArray(options.formData)) { return options.formData; - } else if (options.formData) { + } + if ($.type(options.formData) === 'object') { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -209,28 +304,66 @@ return total; }, + _initProgressObject: function (obj) { + var progress = { + loaded: 0, + total: 0, + bitrate: 0 + }; + if (obj._progress) { + $.extend(obj._progress, progress); + } else { + obj._progress = progress; + } + }, + + _initResponseObject: function (obj) { + var prop; + if (obj._response) { + for (prop in obj._response) { + if (obj._response.hasOwnProperty(prop)) { + delete obj._response[prop]; + } + } + } else { + obj._response = {}; + } + }, + _onProgress: function (e, data) { if (e.lengthComputable) { - var total = data.total || this._getTotal(data.files), - loaded = parseInt( - e.loaded / e.total * (data.chunkSize || total), - 10 - ) + (data.uploadedBytes || 0); - this._loaded += loaded - (data.loaded || data.uploadedBytes || 0); - data.lengthComputable = true; - data.loaded = loaded; - data.total = total; + var now = ((Date.now) ? Date.now() : (new Date()).getTime()), + loaded; + if (data._time && data.progressInterval && + (now - data._time < data.progressInterval) && + e.loaded !== e.total) { + return; + } + data._time = now; + loaded = Math.floor( + e.loaded / e.total * (data.chunkSize || data._progress.total) + ) + (data.uploadedBytes || 0); + // Add the difference from the previously loaded state + // to the global loaded counter: + this._progress.loaded += (loaded - data._progress.loaded); + this._progress.bitrate = this._bitrateTimer.getBitrate( + now, + this._progress.loaded, + data.bitrateInterval + ); + data._progress.loaded = data.loaded = loaded; + data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate( + now, + loaded, + data.bitrateInterval + ); // Trigger a custom progress event with a total data property set // to the file size(s) of the current upload and a loaded data // property calculated accordingly: this._trigger('progress', e, data); // Trigger a global progress event for all current file uploads, // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, { - lengthComputable: true, - loaded: this._loaded, - total: this._total - }); + this._trigger('progressall', e, this._progress); } }, @@ -254,34 +387,30 @@ } }, + _isInstanceOf: function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }, + _initXHRData: function (options) { - var formData, + var that = this, + formData, file = options.files[0], // Ignore non-multipart setting if not supported: - multipart = options.multipart || !$.support.xhrFileUpload; - if (!multipart || options.blob) { - // For non-multipart uploads and chunked uploads, - // file meta data is not part of the request body, - // so we transmit this data as part of the HTTP headers. - // For cross domain requests, these headers must be allowed - // via Access-Control-Allow-Headers or removed using - // the beforeSend callback: - options.headers = $.extend(options.headers, { - 'X-File-Name': file.name, - 'X-File-Type': file.type, - 'X-File-Size': file.size - }); - if (!options.blob) { - // Non-chunked non-multipart upload: - options.contentType = file.type; - options.data = file; - } else if (!multipart) { - // Chunked non-multipart upload: - options.contentType = 'application/octet-stream'; - options.data = options.blob; - } + multipart = options.multipart || !$.support.xhrFileUpload, + paramName = options.paramName[0]; + options.headers = options.headers || {}; + if (options.contentRange) { + options.headers['Content-Range'] = options.contentRange; + } + if (!multipart || options.blob || !this._isInstanceOf('File', file)) { + options.headers['Content-Disposition'] = 'attachment; filename="' + + encodeURI(file.name) + '"'; } - if (multipart && $.support.xhrFormDataFileUpload) { + if (!multipart) { + options.contentType = file.type; + options.data = options.blob || file; + } else if ($.support.xhrFormDataFileUpload) { if (options.postMessage) { // window.postMessage does not allow sending FormData // objects, so we just add the File/Blob objects to @@ -290,19 +419,19 @@ formData = this._getFormData(options); if (options.blob) { formData.push({ - name: options.paramName, + name: paramName, value: options.blob }); } else { $.each(options.files, function (index, file) { formData.push({ - name: options.paramName, + name: options.paramName[index] || paramName, value: file }); }); } } else { - if (options.formData instanceof FormData) { + if (that._isInstanceOf('FormData', options.formData)) { formData = options.formData; } else { formData = new FormData(); @@ -311,14 +440,18 @@ }); } if (options.blob) { - formData.append(options.paramName, options.blob, file.name); + formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { - // File objects are also Blob instances. // This check allows the tests to run with // dummy objects: - if (file instanceof Blob) { - formData.append(options.paramName, file, file.name); + if (that._isInstanceOf('File', file) || + that._isInstanceOf('Blob', file)) { + formData.append( + options.paramName[index] || paramName, + file, + file.name + ); } }); } @@ -330,13 +463,13 @@ }, _initIframeSettings: function (options) { + var targetHost = $('').prop('href', options.url).prop('host'); // Setting the dataType to iframe enables the iframe transport: options.dataType = 'iframe ' + (options.dataType || ''); // The iframe transport accepts a serialized array as form data: options.formData = this._getFormData(options); // Add redirect url to form data on cross-domain uploads: - if (options.redirect && $('').prop('href', options.url) - .prop('host') !== location.host) { + if (options.redirect && targetHost && targetHost !== location.host) { options.formData.push({ name: options.redirectParamName || 'redirect', value: options.redirect @@ -358,29 +491,58 @@ options.dataType = 'postmessage ' + (options.dataType || ''); } } else { - this._initIframeSettings(options, 'iframe'); + this._initIframeSettings(options); } }, + _getParamName: function (options) { + var fileInput = $(options.fileInput), + paramName = options.paramName; + if (!paramName) { + paramName = []; + fileInput.each(function () { + var input = $(this), + name = input.prop('name') || 'files[]', + i = (input.prop('files') || [1]).length; + while (i) { + paramName.push(name); + i -= 1; + } + }); + if (!paramName.length) { + paramName = [fileInput.prop('name') || 'files[]']; + } + } else if (!$.isArray(paramName)) { + paramName = [paramName]; + } + return paramName; + }, + _initFormSettings: function (options) { // Retrieve missing options from the input field and the // associated form, if available: if (!options.form || !options.form.length) { options.form = $(options.fileInput.prop('form')); + // If the given file input doesn't have an associated form, + // use the default widget file input's form: + if (!options.form.length) { + options.form = $(this.options.fileInput.prop('form')); + } } - if (!options.paramName) { - options.paramName = options.fileInput.prop('name') || - 'files[]'; - } + options.paramName = this._getParamName(options); if (!options.url) { options.url = options.form.prop('action') || location.href; } // The HTTP request method must be "POST" or "PUT": options.type = (options.type || options.form.prop('method') || '') .toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT') { + if (options.type !== 'POST' && options.type !== 'PUT' && + options.type !== 'PATCH') { options.type = 'POST'; } + if (!options.formAcceptCharset) { + options.formAcceptCharset = options.form.attr('accept-charset'); + } }, _getAJAXSettings: function (data) { @@ -390,6 +552,21 @@ return options; }, + // jQuery 1.6 doesn't provide .state(), + // while jQuery 1.8+ removed .isRejected() and .isResolved(): + _getDeferredState: function (deferred) { + if (deferred.state) { + return deferred.state(); + } + if (deferred.isResolved()) { + return 'resolved'; + } + if (deferred.isRejected()) { + return 'rejected'; + } + return 'pending'; + }, + // Maps jqXHR callbacks to the equivalent // methods of the given Promise object: _enhancePromise: function (promise) { @@ -414,24 +591,77 @@ return this._enhancePromise(promise); }, + // Adds convenience methods to the data callback argument: + _addConvenienceMethods: function (e, data) { + var that = this, + getPromise = function (data) { + return $.Deferred().resolveWith(that, [data]).promise(); + }; + data.process = function (resolveFunc, rejectFunc) { + if (resolveFunc || rejectFunc) { + data._processQueue = this._processQueue = + (this._processQueue || getPromise(this)) + .pipe(resolveFunc, rejectFunc); + } + return this._processQueue || getPromise(this); + }; + data.submit = function () { + if (this.state() !== 'pending') { + data.jqXHR = this.jqXHR = + (that._trigger('submit', e, this) !== false) && + that._onSend(e, this); + } + return this.jqXHR || that._getXHRPromise(); + }; + data.abort = function () { + if (this.jqXHR) { + return this.jqXHR.abort(); + } + return that._getXHRPromise(); + }; + data.state = function () { + if (this.jqXHR) { + return that._getDeferredState(this.jqXHR); + } + if (this._processQueue) { + return that._getDeferredState(this._processQueue); + } + }; + data.progress = function () { + return this._progress; + }; + data.response = function () { + return this._response; + }; + }, + + // Parses the Range header from the server response + // and returns the uploaded bytes: + _getUploadedBytes: function (jqXHR) { + var range = jqXHR.getResponseHeader('Range'), + parts = range && range.split('-'), + upperBytesPos = parts && parts.length > 1 && + parseInt(parts[1], 10); + return upperBytesPos && upperBytesPos + 1; + }, + // Uploads a file in multiple, sequential requests // by splitting the file up in multiple blob chunks. // If the second parameter is true, only tests if the file // should be uploaded in chunks, but does not invoke any // upload requests: _chunkedUpload: function (options, testOnly) { + options.uploadedBytes = options.uploadedBytes || 0; var that = this, file = options.files[0], fs = file.size, - ub = options.uploadedBytes = options.uploadedBytes || 0, + ub = options.uploadedBytes, mcs = options.maxChunkSize || fs, - // Use the Blob methods with the slice implementation - // according to the W3C Blob API specification: - slice = file.webkitSlice || file.mozSlice || file.slice, - upload, - n, + slice = this._blobSlice, + dfd = $.Deferred(), + promise = dfd.promise(), jqXHR, - pipe; + upload; if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || options.data) { return false; @@ -440,62 +670,84 @@ return true; } if (ub >= fs) { - file.error = 'uploadedBytes'; + file.error = options.i18n('uploadedBytes'); return this._getXHRPromise( false, options.context, [null, 'error', file.error] ); } - // n is the number of blobs to upload, - // calculated via filesize, uploaded bytes and max chunk size: - n = Math.ceil((fs - ub) / mcs); - // The chunk upload method accepting the chunk number as parameter: - upload = function (i) { - if (!i) { - return that._getXHRPromise(true, options.context); - } - // Upload the blobs in sequential order: - return upload(i -= 1).pipe(function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options); - o.blob = slice.call( - file, - ub + i * mcs, - ub + (i + 1) * mcs - ); - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) - .done(function () { - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: o.chunkSize, - total: o.chunkSize - }), o); - } - options.uploadedBytes = o.uploadedBytes += - o.chunkSize; - }); - return jqXHR; - }); + // The chunk upload method: + upload = function () { + // Clone the options object for each chunk upload: + var o = $.extend({}, options), + currentLoaded = o._progress.loaded; + o.blob = slice.call( + file, + ub, + ub + mcs, + file.type + ); + // Store the current chunk size, as the blob itself + // will be dereferenced after data processing: + o.chunkSize = o.blob.size; + // Expose the chunk bytes position range: + o.contentRange = 'bytes ' + ub + '-' + + (ub + o.chunkSize - 1) + '/' + fs; + // Process the upload data (the blob and potential form data): + that._initXHRData(o); + // Add progress listeners for this chunk upload: + that._initProgressListener(o); + jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) || + that._getXHRPromise(false, o.context)) + .done(function (result, textStatus, jqXHR) { + ub = that._getUploadedBytes(jqXHR) || + (ub + o.chunkSize); + // Create a progress event if no final progress event + // with loaded equaling total has been triggered + // for this chunk: + if (currentLoaded + o.chunkSize - o._progress.loaded) { + that._onProgress($.Event('progress', { + lengthComputable: true, + loaded: ub - o.uploadedBytes, + total: ub - o.uploadedBytes + }), o); + } + options.uploadedBytes = o.uploadedBytes = ub; + o.result = result; + o.textStatus = textStatus; + o.jqXHR = jqXHR; + that._trigger('chunkdone', null, o); + that._trigger('chunkalways', null, o); + if (ub < fs) { + // File upload not yet complete, + // continue with the next chunk: + upload(); + } else { + dfd.resolveWith( + o.context, + [result, textStatus, jqXHR] + ); + } + }) + .fail(function (jqXHR, textStatus, errorThrown) { + o.jqXHR = jqXHR; + o.textStatus = textStatus; + o.errorThrown = errorThrown; + that._trigger('chunkfail', null, o); + that._trigger('chunkalways', null, o); + dfd.rejectWith( + o.context, + [jqXHR, textStatus, errorThrown] + ); + }); }; - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe = upload(n); - pipe.abort = function () { + this._enhancePromise(promise); + promise.abort = function () { return jqXHR.abort(); }; - return this._enhancePromise(pipe); + upload(); + return promise; }, _beforeSend: function (e, data) { @@ -504,99 +756,113 @@ // and no other uploads are currently running, // equivalent to the global ajaxStart event: this._trigger('start'); + // Set timer for global bitrate progress calculation: + this._bitrateTimer = new this._BitrateTimer(); + // Reset the global progress values: + this._progress.loaded = this._progress.total = 0; + this._progress.bitrate = 0; } + // Make sure the container objects for the .response() and + // .progress() methods on the data object are available + // and reset to their initial state: + this._initResponseObject(data); + this._initProgressObject(data); + data._progress.loaded = data.loaded = data.uploadedBytes || 0; + data._progress.total = data.total = this._getTotal(data.files) || 1; + data._progress.bitrate = data.bitrate = 0; this._active += 1; // Initialize the global progress values: - this._loaded += data.uploadedBytes || 0; - this._total += this._getTotal(data.files); + this._progress.loaded += data.loaded; + this._progress.total += data.total; }, _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options)) { - // Create a progress event for each iframe load: + var total = options._progress.total, + response = options._response; + if (options._progress.loaded < total) { + // Create a progress event if no final progress event + // with loaded equaling total has been triggered: this._onProgress($.Event('progress', { lengthComputable: true, - loaded: 1, - total: 1 + loaded: total, + total: total }), options); } - options.result = result; - options.textStatus = textStatus; - options.jqXHR = jqXHR; + response.result = options.result = result; + response.textStatus = options.textStatus = textStatus; + response.jqXHR = options.jqXHR = jqXHR; this._trigger('done', null, options); }, _onFail: function (jqXHR, textStatus, errorThrown, options) { - options.jqXHR = jqXHR; - options.textStatus = textStatus; - options.errorThrown = errorThrown; - this._trigger('fail', null, options); + var response = options._response; if (options.recalculateProgress) { // Remove the failed (error or abort) file upload from // the global progress calculation: - this._loaded -= options.loaded || options.uploadedBytes || 0; - this._total -= options.total || this._getTotal(options.files); + this._progress.loaded -= options._progress.loaded; + this._progress.total -= options._progress.total; } + response.jqXHR = options.jqXHR = jqXHR; + response.textStatus = options.textStatus = textStatus; + response.errorThrown = options.errorThrown = errorThrown; + this._trigger('fail', null, options); }, _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { - this._active -= 1; - options.textStatus = textStatus; - if (jqXHRorError && jqXHRorError.always) { - options.jqXHR = jqXHRorError; - options.result = jqXHRorResult; - } else { - options.jqXHR = jqXHRorResult; - options.errorThrown = jqXHRorError; - } + // jqXHRorResult, textStatus and jqXHRorError are added to the + // options object via done and fail callbacks this._trigger('always', null, options); - if (this._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - this._trigger('stop'); - // Reset the global progress values: - this._loaded = this._total = 0; - } }, _onSend: function (e, data) { + if (!data.submit) { + this._addConvenienceMethods(e, data); + } var that = this, jqXHR, + aborted, slot, pipe, options = that._getAJAXSettings(data), - send = function (resolve, args) { + send = function () { that._sending += 1; + // Set timer for bitrate progress calculation: + options._bitrateTimer = new that._BitrateTimer(); jqXHR = jqXHR || ( - (resolve !== false && - that._trigger('send', e, options) !== false && - (that._chunkedUpload(options) || $.ajax(options))) || - that._getXHRPromise(false, options.context, args) + ((aborted || that._trigger('send', e, options) === false) && + that._getXHRPromise(false, options.context, aborted)) || + that._chunkedUpload(options) || $.ajax(options) ).done(function (result, textStatus, jqXHR) { that._onDone(result, textStatus, jqXHR, options); }).fail(function (jqXHR, textStatus, errorThrown) { that._onFail(jqXHR, textStatus, errorThrown, options); }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._sending -= 1; that._onAlways( jqXHRorResult, textStatus, jqXHRorError, options ); + that._sending -= 1; + that._active -= 1; if (options.limitConcurrentUploads && options.limitConcurrentUploads > that._sending) { // Start the next queued upload, // that has not been aborted: var nextSlot = that._slots.shift(); while (nextSlot) { - if (!nextSlot.isRejected()) { + if (that._getDeferredState(nextSlot) === 'pending') { nextSlot.resolve(); break; } nextSlot = that._slots.shift(); } } + if (that._active === 0) { + // The stop callback is triggered when all uploads have + // been completed, equivalent to the global ajaxStop event: + that._trigger('stop'); + } }); return jqXHR; }; @@ -609,18 +875,19 @@ this._slots.push(slot); pipe = slot.pipe(send); } else { - pipe = (this._sequence = this._sequence.pipe(send, send)); + this._sequence = this._sequence.pipe(send, send); + pipe = this._sequence; } // Return the piped Promise object, enhanced with an abort method, // which is delegated to the jqXHR object of the current upload, // and jqXHR callbacks mapped to the equivalent Promise methods: pipe.abort = function () { - var args = [undefined, 'abort', 'abort']; + aborted = [undefined, 'abort', 'abort']; if (!jqXHR) { if (slot) { - slot.rejectWith(args); + slot.rejectWith(options.context, aborted); } - return send(false, args); + return send(); } return jqXHR.abort(); }; @@ -634,40 +901,43 @@ result = true, options = $.extend({}, this.options, data), limit = options.limitMultiFileUploads, + paramName = this._getParamName(options), + paramNameSet, + paramNameSlice, fileSet, i; if (!(options.singleFileUploads || limit) || !this._isXHRUpload(options)) { fileSet = [data.files]; + paramNameSet = [paramName]; } else if (!options.singleFileUploads && limit) { fileSet = []; + paramNameSet = []; for (i = 0; i < data.files.length; i += limit) { fileSet.push(data.files.slice(i, i + limit)); + paramNameSlice = paramName.slice(i, i + limit); + if (!paramNameSlice.length) { + paramNameSlice = paramName; + } + paramNameSet.push(paramNameSlice); } + } else { + paramNameSet = paramName; } data.originalFiles = data.files; $.each(fileSet || data.files, function (index, element) { - var files = fileSet ? element : [element], - newData = $.extend({}, data, {files: files}); - newData.submit = function () { - newData.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); - return this.jqXHR; - }; - return (result = that._trigger('add', e, newData)); + var newData = $.extend({}, data); + newData.files = fileSet ? element : [element]; + newData.paramName = paramNameSet[index]; + that._initResponseObject(newData); + that._initProgressObject(newData); + that._addConvenienceMethods(e, newData); + result = that._trigger('add', e, newData); + return result; }); return result; }, - // File Normalization for Gecko 1.9.1 (Firefox 3.5) support: - _normalizeFile: function (index, file) { - if (file.name === undefined && file.size === undefined) { - file.name = file.fileName; - file.size = file.fileSize; - } - }, - _replaceFileInput: function (input) { var inputClone = input.clone(true); $('
').append(inputClone)[0].reset(); @@ -677,7 +947,7 @@ // Avoid memory leaks with the detached file input: $.cleanData(input.unbind('remove')); // Replace the original file input element in the fileInput - // collection with the clone, which has been copied including + // elements set with the clone, which has been copied including // event handlers: this.options.fileInput = this.options.fileInput.map(function (i, el) { if (el === input[0]) { @@ -692,102 +962,229 @@ } }, - _onChange: function (e) { - var that = e.data.fileupload, - data = { - files: $.each($.makeArray(e.target.files), that._normalizeFile), - fileInput: $(e.target), - form: $(e.target.form) - }; - if (!data.files.length) { + _handleFileTreeEntry: function (entry, path) { + var that = this, + dfd = $.Deferred(), + errorHandler = function (e) { + if (e && !e.entry) { + e.entry = entry; + } + // Since $.when returns immediately if one + // Deferred is rejected, we use resolve instead. + // This allows valid files and invalid items + // to be returned together in one set: + dfd.resolve([e]); + }, + dirReader; + path = path || ''; + if (entry.isFile) { + if (entry._file) { + // Workaround for Chrome bug #149735 + entry._file.relativePath = path; + dfd.resolve(entry._file); + } else { + entry.file(function (file) { + file.relativePath = path; + dfd.resolve(file); + }, errorHandler); + } + } else if (entry.isDirectory) { + dirReader = entry.createReader(); + dirReader.readEntries(function (entries) { + that._handleFileTreeEntries( + entries, + path + entry.name + '/' + ).done(function (files) { + dfd.resolve(files); + }).fail(errorHandler); + }, errorHandler); + } else { + // Return an empy list for file system items + // other than files or directories: + dfd.resolve([]); + } + return dfd.promise(); + }, + + _handleFileTreeEntries: function (entries, path) { + var that = this; + return $.when.apply( + $, + $.map(entries, function (entry) { + return that._handleFileTreeEntry(entry, path); + }) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _getDroppedFiles: function (dataTransfer) { + dataTransfer = dataTransfer || {}; + var items = dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry || + items[0].getAsEntry)) { + return this._handleFileTreeEntries( + $.map(items, function (item) { + var entry; + if (item.webkitGetAsEntry) { + entry = item.webkitGetAsEntry(); + if (entry) { + // Workaround for Chrome bug #149735: + entry._file = item.getAsFile(); + } + return entry; + } + return item.getAsEntry(); + }) + ); + } + return $.Deferred().resolve( + $.makeArray(dataTransfer.files) + ).promise(); + }, + + _getSingleFileInputFiles: function (fileInput) { + fileInput = $(fileInput); + var entries = fileInput.prop('webkitEntries') || + fileInput.prop('entries'), + files, + value; + if (entries && entries.length) { + return this._handleFileTreeEntries(entries); + } + files = $.makeArray(fileInput.prop('files')); + if (!files.length) { + value = fileInput.prop('value'); + if (!value) { + return $.Deferred().resolve([]).promise(); + } // If the files property is not available, the browser does not // support the File API and we add a pseudo File object with // the input value as name with path information removed: - data.files = [{name: e.target.value.replace(/^.*\\/, '')}]; - } - if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); + files = [{name: value.replace(/^.*\\/, '')}]; + } else if (files[0].name === undefined && files[0].fileName) { + // File normalization for Safari 4 and Firefox 3: + $.each(files, function (index, file) { + file.name = file.fileName; + file.size = file.fileSize; + }); } - if (that._trigger('change', e, data) === false || - that._onAdd(e, data) === false) { - return false; + return $.Deferred().resolve(files).promise(); + }, + + _getFileInputFiles: function (fileInput) { + if (!(fileInput instanceof $) || fileInput.length === 1) { + return this._getSingleFileInputFiles(fileInput); } + return $.when.apply( + $, + $.map(fileInput, this._getSingleFileInputFiles) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _onChange: function (e) { + var that = this, + data = { + fileInput: $(e.target), + form: $(e.target.form) + }; + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + if (that.options.replaceFileInput) { + that._replaceFileInput(data.fileInput); + } + if (that._trigger('change', e, data) !== false) { + that._onAdd(e, data); + } + }); }, _onPaste: function (e) { - var that = e.data.fileupload, - cbd = e.originalEvent.clipboardData, - items = (cbd && cbd.items) || [], + var items = e.originalEvent && e.originalEvent.clipboardData && + e.originalEvent.clipboardData.items, data = {files: []}; - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); + if (items && items.length) { + $.each(items, function (index, item) { + var file = item.getAsFile && item.getAsFile(); + if (file) { + data.files.push(file); + } + }); + if (this._trigger('paste', e, data) === false || + this._onAdd(e, data) === false) { + return false; } - }); - if (that._trigger('paste', e, data) === false || - that._onAdd(e, data) === false) { - return false; } }, _onDrop: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, - data = { - files: $.each( - $.makeArray(dataTransfer && dataTransfer.files), - that._normalizeFile - ) - }; - if (that._trigger('drop', e, data) === false || - that._onAdd(e, data) === false) { - return false; + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var that = this, + dataTransfer = e.dataTransfer, + data = {}; + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + e.preventDefault(); + this._getDroppedFiles(dataTransfer).always(function (files) { + data.files = files; + if (that._trigger('drop', e, data) !== false) { + that._onAdd(e, data); + } + }); } - e.preventDefault(); }, _onDragOver: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (that._trigger('dragover', e) === false) { - return false; - } + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var dataTransfer = e.dataTransfer; if (dataTransfer) { - dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy'; + if (this._trigger('dragover', e) === false) { + return false; + } + if ($.inArray('Files', dataTransfer.types) !== -1) { + dataTransfer.dropEffect = 'copy'; + e.preventDefault(); + } } - e.preventDefault(); }, _initEventHandlers: function () { - var ns = this.options.namespace; if (this._isXHRUpload(this.options)) { - this.options.dropZone - .bind('dragover.' + ns, {fileupload: this}, this._onDragOver) - .bind('drop.' + ns, {fileupload: this}, this._onDrop) - .bind('paste.' + ns, {fileupload: this}, this._onPaste); + this._on(this.options.dropZone, { + dragover: this._onDragOver, + drop: this._onDrop + }); + this._on(this.options.pasteZone, { + paste: this._onPaste + }); + } + if ($.support.fileInput) { + this._on(this.options.fileInput, { + change: this._onChange + }); } - this.options.fileInput - .bind('change.' + ns, {fileupload: this}, this._onChange); }, _destroyEventHandlers: function () { - var ns = this.options.namespace; - this.options.dropZone - .unbind('dragover.' + ns, this._onDragOver) - .unbind('drop.' + ns, this._onDrop) - .unbind('paste.' + ns, this._onPaste); - this.options.fileInput - .unbind('change.' + ns, this._onChange); + this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.pasteZone, 'paste'); + this._off(this.options.fileInput, 'change'); }, _setOption: function (key, value) { - var refresh = $.inArray(key, this._refreshOptionsList) !== -1; - if (refresh) { + var reinit = $.inArray(key, this._specialOptions) !== -1; + if (reinit) { this._destroyEventHandlers(); } - $.Widget.prototype._setOption.call(this, key, value); - if (refresh) { + this._super(key, value); + if (reinit) { this._initSpecialOptions(); this._initEventHandlers(); } @@ -796,42 +1193,68 @@ _initSpecialOptions: function () { var options = this.options; if (options.fileInput === undefined) { - options.fileInput = this.element.is('input:file') ? - this.element : this.element.find('input:file'); + options.fileInput = this.element.is('input[type="file"]') ? + this.element : this.element.find('input[type="file"]'); } else if (!(options.fileInput instanceof $)) { options.fileInput = $(options.fileInput); } if (!(options.dropZone instanceof $)) { options.dropZone = $(options.dropZone); } + if (!(options.pasteZone instanceof $)) { + options.pasteZone = $(options.pasteZone); + } + }, + + _getRegExp: function (str) { + var parts = str.split('/'), + modifiers = parts.pop(); + parts.shift(); + return new RegExp(parts.join('/'), modifiers); + }, + + _isRegExpOption: function (key, value) { + return key !== 'url' && $.type(value) === 'string' && + /^\/.*\/[igm]{0,3}$/.test(value); + }, + + _initDataAttributes: function () { + var that = this, + options = this.options; + // Initialize options set via HTML5 data-attributes: + $.each( + $(this.element[0].cloneNode(false)).data(), + function (key, value) { + if (that._isRegExpOption(key, value)) { + value = that._getRegExp(value); + } + options[key] = value; + } + ); }, _create: function () { - var options = this.options, - dataOpts = $.extend({}, this.element.data()); - dataOpts[this.widgetName] = undefined; - $.extend(options, dataOpts); - options.namespace = options.namespace || this.widgetName; + this._initDataAttributes(); this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); - this._sending = this._active = this._loaded = this._total = 0; + this._sending = this._active = 0; + this._initProgressObject(this); this._initEventHandlers(); }, - destroy: function () { - this._destroyEventHandlers(); - $.Widget.prototype.destroy.call(this); - }, - - enable: function () { - $.Widget.prototype.enable.call(this); - this._initEventHandlers(); + // This method is exposed to the widget API and allows to query + // the number of active uploads: + active: function () { + return this._active; }, - disable: function () { - this._destroyEventHandlers(); - $.Widget.prototype.disable.call(this); + // This method is exposed to the widget API and allows to query + // the widget upload progress. + // It returns an object with loaded, total and bitrate properties + // for the running uploads: + progress: function () { + return this._progress; }, // This method is exposed to the widget API and allows adding files @@ -839,21 +1262,65 @@ // must have a files property and can contain additional options: // .fileupload('add', {files: filesList}); add: function (data) { + var that = this; if (!data || this.options.disabled) { return; } - data.files = $.each($.makeArray(data.files), this._normalizeFile); - this._onAdd(null, data); + if (data.fileInput && !data.files) { + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + that._onAdd(null, data); + }); + } else { + data.files = $.makeArray(data.files); + this._onAdd(null, data); + } }, // This method is exposed to the widget API and allows sending files // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: + // must have a files or fileInput property and can contain additional options: // .fileupload('send', {files: filesList}); // The method returns a Promise object for the file upload call. send: function (data) { if (data && !this.options.disabled) { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + if (data.fileInput && !data.files) { + var that = this, + dfd = $.Deferred(), + promise = dfd.promise(), + jqXHR, + aborted; + promise.abort = function () { + aborted = true; + if (jqXHR) { + return jqXHR.abort(); + } + dfd.reject(null, 'abort', 'abort'); + return promise; + }; + this._getFileInputFiles(data.fileInput).always( + function (files) { + if (aborted) { + return; + } + if (!files.length) { + dfd.reject(); + return; + } + data.files = files; + jqXHR = that._onSend(null, data).then( + function (result, textStatus, jqXHR) { + dfd.resolve(result, textStatus, jqXHR); + }, + function (jqXHR, textStatus, errorThrown) { + dfd.reject(jqXHR, textStatus, errorThrown); + } + ); + } + ); + return this._enhancePromise(promise); + } + data.files = $.makeArray(data.files); if (data.files.length) { return this._onSend(null, data); } @@ -863,4 +1330,4 @@ }); -})); +})); \ No newline at end of file diff --git a/apps/files/js/jquery.iframe-transport.js b/apps/files/js/jquery.iframe-transport.js index d85c0c11297..5c9df77976b 100644 --- a/apps/files/js/jquery.iframe-transport.js +++ b/apps/files/js/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.3 + * jQuery Iframe Transport Plugin 1.7 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -30,27 +30,45 @@ // The iframe transport accepts three additional options: // options.fileInput: a jQuery collection of file input fields // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s) + // overrides the name property of the file input field(s), + // can be a string or an array of strings. // options.formData: an array of objects with name and value properties, // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { + if (options.async) { var form, - iframe; + iframe, + addParamChar; return { send: function (_, completeCallback) { form = $('
'); + form.attr('accept-charset', options.formAcceptCharset); + addParamChar = /\?/.test(options.url) ? '&' : '?'; + // XDomainRequest only supports GET and POST: + if (options.type === 'DELETE') { + options.url = options.url + addParamChar + '_method=DELETE'; + options.type = 'POST'; + } else if (options.type === 'PUT') { + options.url = options.url + addParamChar + '_method=PUT'; + options.type = 'POST'; + } else if (options.type === 'PATCH') { + options.url = options.url + addParamChar + '_method=PATCH'; + options.type = 'POST'; + } // javascript:false as initial iframe src // prevents warning popups on HTTPS in IE6. // IE versions below IE8 cannot set the name property of // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: + counter += 1; iframe = $( '' + counter + '">' ).bind('load', function () { - var fileInputClones; + var fileInputClones, + paramNames = $.isArray(options.paramName) ? + options.paramName : [options.paramName]; iframe .unbind('load') .bind('load', function () { @@ -79,7 +97,12 @@ // (happens on form submits to iframe targets): $('') .appendTo(form); - form.remove(); + window.setTimeout(function () { + // Removing the form in a setTimeout call + // allows Chrome's developer tools to display + // the response result + form.remove(); + }, 0); }); form .prop('target', iframe.prop('name')) @@ -101,8 +124,11 @@ return fileInputClones[index]; }); if (options.paramName) { - options.fileInput.each(function () { - $(this).prop('name', options.paramName); + options.fileInput.each(function (index) { + $(this).prop( + 'name', + paramNames[index] || options.paramName + ); }); } // Appending the file input fields to the hidden form @@ -144,22 +170,36 @@ }); // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: + // The following adds converters from iframe to text, json, html, xml + // and script. + // Please note that the Content-Type for JSON responses has to be text/plain + // or text/html, if the browser doesn't include application/json in the + // Accept header, else IE will show a download dialog. + // The Content-Type for XML responses on the other hand has to be always + // application/xml or text/xml, so IE properly parses the XML response. + // See also + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation $.ajaxSetup({ converters: { 'iframe text': function (iframe) { - return $(iframe[0].body).text(); + return iframe && $(iframe[0].body).text(); }, 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); + return iframe && $.parseJSON($(iframe[0].body).text()); }, 'iframe html': function (iframe) { - return $(iframe[0].body).html(); + return iframe && $(iframe[0].body).html(); + }, + 'iframe xml': function (iframe) { + var xmlDoc = iframe && iframe[0]; + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || + $(xmlDoc.body).html()); }, 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); + return iframe && $.globalEval($(iframe[0].body).text()); } } }); -})); +})); \ No newline at end of file diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 0c7d6936697..39f5ac471e5 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,7 +1,8 @@ +$totalsize = 0; +$pc = 0; ?> $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); if($relative_date_color>160) $relative_date_color = 160; $name = \OCP\Util::encodePath($file['name']); - $directory = \OCP\Util::encodePath($file['directory']); ?> + $directory = \OCP\Util::encodePath($file['directory']); + ?> + + + )"> -- cgit v1.2.3 From bbf8acb383bdcb1dcb53f4b9d5a8d894b17401df Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 5 Sep 2013 10:19:54 +0200 Subject: separate uploading code from progress code, add progress capability detection --- apps/files/js/file-upload.js | 193 +++++++++++++++++++++++-------------------- apps/files/js/filelist.js | 20 ++--- apps/files/js/files.js | 32 +++---- core/js/oc-dialogs.js | 13 ++- 4 files changed, 136 insertions(+), 122 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index be3d7e08aff..bd0ae4db00b 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -14,6 +14,7 @@ * - when only existing -> remember as single skip action * - when only new -> remember as single replace action * - when both -> remember as single autorename action + * - continue -> apply marks, when nothing is marked continue == skip all * - start uploading selection * * on send @@ -96,7 +97,30 @@ * */ +// from https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html +// also see article at http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata +// Function that will allow us to know if Ajax uploads are supported +function supportAjaxUploadWithProgress() { + 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; + } +} //TODO clean uploads when all progress has completed OC.Upload = { @@ -245,6 +269,7 @@ OC.Upload = { console.log(data); }, checkExistingFiles: function (selection, callbacks){ + // FIXME check filelist before uploading callbacks.onNoConflicts(selection); } }; @@ -327,7 +352,7 @@ $(document).ready(function() { return false; //don't upload anything } - // check existing files whan all is collected + // check existing files when all is collected if ( selection.uploads.length >= selection.filesToUpload ) { //remove our selection hack: @@ -358,11 +383,6 @@ $(document).ready(function() { OC.Upload.checkExistingFiles(selection, callbacks); - //TODO refactor away: - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } } @@ -389,13 +409,6 @@ $(document).ready(function() { */ start: function(e) { OC.Upload.logStatus('start', e, null); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return true; - } - $('#uploadprogresswrapper input.stop').show(); - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { OC.Upload.logStatus('fail', e, data); @@ -414,32 +427,6 @@ $(document).ready(function() { } //var selection = OC.Upload.getSelection(data.originalFiles); //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - - //if user pressed cancel hide upload progress bar and cancel button - if (data.errorThrown === 'abort') { - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - } - }, - progress: function(e, data) { - OC.Upload.logStatus('progress', e, data); - // TODO: show nice progress bar in file row - }, - /** - * - * @param {type} e - * @param {type} data (only has loaded, total and lengthComputable) - * @returns {unresolved} - */ - progressall: function(e, data) { - OC.Upload.logStatus('progressall', e, data); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - var progress = (data.loaded/data.total)*100; - //var progress = OC.Upload.progressBytes(); - $('#uploadprogressbar').progressbar('value', progress); }, /** * called for every successful upload @@ -460,33 +447,21 @@ $(document).ready(function() { //var selection = OC.Upload.getSelection(data.originalFiles); if(typeof result[0] !== 'undefined' - && result[0].status === 'success' + && result[0].status === 'existserror' ) { - //if (selection) { - // selection.loadedBytes+=data.loaded; - //} - //OC.Upload.nextUpload(); + //show "file already exists" dialog + var original = result[0]; + var replacement = data.files[0]; + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - if (result[0].status === 'existserror') { - //show "file already exists" dialog - var original = result[0]; - var replacement = data.files[0]; - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); - } else { - OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + data.textStatus = 'servererror'; + data.errorThrown = t('files', result.data.message); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); } - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} }, /** @@ -496,36 +471,78 @@ $(document).ready(function() { */ stop: function(e, data) { OC.Upload.logStatus('stop', e, data); - //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading - - //OC.Upload.cancelUploads(); //cleanup - - // if(data.dataType !== 'iframe') { - // $('#uploadprogresswrapper input.stop').hide(); - // } - - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - - // $('#uploadprogressbar').progressbar('value', 100); - // $('#uploadprogressbar').fadeOut(); - //} - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} } }; - - var file_upload_handler = function() { - $('#file_upload_start').fileupload(file_upload_param); - }; if ( document.getElementById('data-upload-form') ) { - $(file_upload_handler); + // initialize jquery fileupload (blueimp) + var fileupload = $('#file_upload_start').fileupload(file_upload_param); + + if(supportAjaxUploadWithProgress()) { + + // add progress handlers + fileupload.on('fileuploadadd', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadadd', e, data); + //show cancel button + //if(data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? + // $('#uploadprogresswrapper input.stop').show(); + //} + }); + // add progress handlers + fileupload.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstart', e, data); + $('#uploadprogresswrapper input.stop').show(); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }); + fileupload.on('fileuploadprogress', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogress', e, data); + //TODO progressbar in row + }); + fileupload.on('fileuploadprogressall', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogressall', e, data); + var progress = (data.loaded / data.total) * 100; + $('#uploadprogressbar').progressbar('value', progress); + }); + fileupload.on('fileuploaddone', function(e, data) { + OC.Upload.logStatus('progress handle fileuploaddone', e, data); + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadstop', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstop', e, data); + //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + //OC.Upload.cancelUploads(); //cleanup + + // if(data.dataType !== 'iframe') { + // $('#uploadprogresswrapper input.stop').hide(); + // } + + // $('#uploadprogressbar').progressbar('value', 100); + // $('#uploadprogressbar').fadeOut(); + //} + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadfail', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadfail', e, data); + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } + }); + + } else { + console.log('skipping file progress because your browser is broken'); + } } $.assocArraySize = function(obj) { // http://stackoverflow.com/a/6700/11236 diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 31e2a8300e2..4f20d1940aa 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -517,7 +517,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - OC.Upload.logStatus('fileuploaddrop', e, data); + OC.Upload.logStatus('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -556,7 +556,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - OC.Upload.logStatus('fileuploadadd', e, data); + OC.Upload.logStatus('filelist handle fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -592,10 +592,10 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadstart', function(e, data) { - OC.Upload.logStatus('fileuploadstart', e, data); + OC.Upload.logStatus('filelist handle fileuploadstart', e, data); }); file_upload_start.on('fileuploaddone', function(e, data) { - OC.Upload.logStatus('fileuploaddone', e, data); + OC.Upload.logStatus('filelist handle fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -672,22 +672,22 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadalways', function(e, data) { - OC.Upload.logStatus('fileuploadalways', e, data); + OC.Upload.logStatus('filelist handle fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - OC.Upload.logStatus('fileuploadsend', e, data); + OC.Upload.logStatus('filelist handle fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - OC.Upload.logStatus('fileuploadprogress', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - OC.Upload.logStatus('fileuploadprogressall', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - OC.Upload.logStatus('fileuploadstop', e, data); + OC.Upload.logStatus('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (! OC.Upload.isProcessing()) { @@ -700,7 +700,7 @@ $(document).ready(function(){ } }); file_upload_start.on('fileuploadfail', function(e, data) { - OC.Upload.logStatus('fileuploadfail', e, data); + OC.Upload.logStatus('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 9a725fc2077..4a6c9c78900 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,6 +1,6 @@ Files={ updateMaxUploadFilesize:function(response) { - if(response == undefined) { + if(response === undefined) { return; } if(response.data !== undefined && response.data.uploadMaxFilesize !== undefined) { @@ -9,7 +9,7 @@ Files={ $('#usedSpacePercent').val(response.data.usedSpacePercent); Files.displayStorageWarnings(); } - if(response[0] == undefined) { + if(response[0] === undefined) { return; } if(response[0].uploadMaxFilesize !== undefined) { @@ -25,7 +25,7 @@ Files={ OC.Notification.show(t('files', '\'.\' is an invalid file name.')); return false; } - if (name.length == 0) { + if (name.length === 0) { OC.Notification.show(t('files', 'File name cannot be empty.')); return false; } @@ -33,7 +33,7 @@ Files={ // check for invalid characters var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*']; for (var i = 0; i < invalid_characters.length; i++) { - if (name.indexOf(invalid_characters[i]) != -1) { + if (name.indexOf(invalid_characters[i]) !== -1) { OC.Notification.show(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.")); return false; } @@ -127,7 +127,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -145,7 +145,7 @@ $(document).ready(function() { $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().toggleClass('selected'); var selectedCount=$('td.filename input:checkbox:checked').length; - if (selectedCount == $('td.filename input:checkbox').length) { + if (selectedCount === $('td.filename input:checkbox').length) { $('#select_all').attr('checked', 'checked'); } } @@ -192,7 +192,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -205,7 +205,7 @@ $(document).ready(function() { if(!$(this).attr('checked')){ $('#select_all').attr('checked',false); }else{ - if(selectedCount==$('td.filename input:checkbox').length){ + if(selectedCount === $('td.filename input:checkbox').length){ $('#select_all').attr('checked',true); } } @@ -262,9 +262,9 @@ $(document).ready(function() { function resizeBreadcrumbs(firstRun) { var width = $(this).width(); - if (width != lastWidth) { + if (width !== lastWidth) { if ((width < lastWidth || firstRun) && width < breadcrumbsWidth) { - if (hiddenBreadcrumbs == 0) { + if (hiddenBreadcrumbs === 0) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('a').hide(); $(breadcrumbs[1]).append('...'); @@ -276,12 +276,12 @@ $(document).ready(function() { breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; $(breadcrumbs[i]).hide(); hiddenBreadcrumbs = i; - i++ + i++; } } else if (width > lastWidth && hiddenBreadcrumbs > 0) { var i = hiddenBreadcrumbs; while (width > breadcrumbsWidth && i > 0) { - if (hiddenBreadcrumbs == 1) { + if (hiddenBreadcrumbs === 1) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('span').remove(); $(breadcrumbs[1]).find('a').show(); @@ -382,7 +382,7 @@ scanFiles.scanning=false; function boolOperationFinished(data, callback) { result = jQuery.parseJSON(data.responseText); Files.updateMaxUploadFilesize(result); - if(result.status == 'success'){ + if(result.status === 'success'){ callback.call(); } else { alert(result.data.message); @@ -436,7 +436,7 @@ var createDragShadow = function(event){ }); return dragshadow; -} +}; //options for file drag/drop var dragOptions={ @@ -446,7 +446,7 @@ var dragOptions={ stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); } -} +}; // sane browsers support using the distance option if ( $('html.ie').length === 0) { dragOptions['distance'] = 20; @@ -489,7 +489,7 @@ var folderDropOptions={ }); }, tolerance: 'pointer' -} +}; var crumbDropOptions={ drop: function( event, ui ) { diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 5ed24417264..08afbfd42f2 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -257,13 +257,7 @@ var OCdialogs = { //TODO add to same size collection? } - //add checkbox toggling actions - conflict.find('.replacement,.original').on('click', function(){ - var checkbox = $(this).find('input[type="checkbox"]'); - checkbox.prop('checkbox', !checkbox.prop('checkbox')); - }).find('input[type="checkbox"]').prop('checkbox',false); - - //TODO show skip action for files with same size and mtime + //TODO show skip action for files with same size and mtime in bottom row }; var selection = controller.getSelection(data.originalFiles); @@ -345,11 +339,14 @@ var OCdialogs = { var checkboxes = $(dialog_id).find('.conflict:not(.template) .original input[type="checkbox"]'); checkboxes.prop('checked', $(this).prop('checked')); }); - $(dialog_id).find('.conflicts').on('click', '.replacement,.original', function() { var checkbox = $(this).find('input[type="checkbox"]'); checkbox.prop('checked', !checkbox.prop('checked')); }); + $(dialog_id).find('.conflicts').on('click', 'input[type="checkbox"]', function() { + var checkbox = $(this); + checkbox.prop('checked', !checkbox.prop('checked')); + }); //update counters $(dialog_id).on('click', '.replacement,.allnewfiles', function() { -- cgit v1.2.3 From b40925ae1747ae44a52fb1f8dcf7645d022c6f13 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 31 Jul 2013 22:24:52 +0200 Subject: initial scrollto implementation: use places/folder icon, move link construction to JS, only show icon on hover, use 'searchresult' as css class name, add filter/unfilter methods, highlight searched files in current filelist only filter when correct FileList is present --- apps/files/css/files.css | 3 +++ apps/files/js/filelist.js | 41 ++++++++++++++++++++++++++++++++++----- apps/files/js/files.js | 5 +++++ core/js/js.js | 13 +++++++++++++ lib/search/provider/file.php | 3 ++- lib/search/result.php | 4 +++- search/css/results.css | 22 +++++++++++++++++++-- search/js/result.js | 38 ++++++++++++++++++++++++++++++------ search/templates/part.results.php | 3 ++- 9 files changed, 116 insertions(+), 16 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 41d9808c56b..0acb3c5d829 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -76,6 +76,9 @@ #filestable tbody tr.selected { background-color: rgb(230,230,230); } +#filestable tbody tr.searchresult { + background-color: rgb(240,240,240); +} tbody a { color:#000; } span.extension, span.uploading, td.date { color:#999; } span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index b50d46c98d3..9a2d39c3652 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -643,6 +643,37 @@ var FileList={ if (FileList._maskTimeout){ window.clearTimeout(FileList._maskTimeout); } + }, + scrollTo:function(file) { + //scroll to and highlight preselected file + var scrolltorow = $('tr[data-file="'+file+'"]'); + if (scrolltorow.length > 0) { + scrolltorow.addClass('searchresult'); + $(window).scrollTop(scrolltorow.position().top); + //remove highlight when hovered over + scrolltorow.one('hover', function(){ + scrolltorow.removeClass('searchresult'); + }); + } + }, + filter:function(query){ + $('#fileList tr:not(.summary)').each(function(i,e){ + if ($(e).data('file').toLowerCase().indexOf(query.toLowerCase()) !== -1) { + $(e).addClass("searchresult"); + } else { + $(e).removeClass("searchresult"); + } + }); + //do not use scrollto to prevent removing searchresult css class + var first = $('#fileList tr.searchresult').first(); + if (first.length !== 0) { + $(window).scrollTop(first.position().top); + } + }, + unfilter:function(){ + $('#fileList tr.searchresult').each(function(i,e){ + $(e).removeClass("searchresult"); + }); } }; @@ -818,16 +849,16 @@ $(document).ready(function(){ FileList.replaceIsNewFile = null; } FileList.lastAction = null; - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.replace', function() { - OC.Notification.hide(function() { - FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); - }); + OC.Notification.hide(function() { + FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); + }); }); $('#notification:first-child').on('click', '.suggest', function() { $('tr').filterAttr('data-file', $('#notification > span').attr('data-oldName')).show(); - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.cancel', function() { if ($('#notification > span').attr('data-isNewFile')) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index c2418cfa751..a4fdf383339 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -384,6 +384,11 @@ $(document).ready(function() { } }); } + + //scroll to and highlight preselected file + if (getURLParameter('scrollto')) { + FileList.scrollTo(getURLParameter('scrollto')); + } }); function scanFiles(force, dir, users){ diff --git a/core/js/js.js b/core/js/js.js index c09f80369f9..c23cf9eebd9 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -723,11 +723,17 @@ $(document).ready(function(){ } }else if(event.keyCode===27){//esc OC.search.hide(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }else{ var query=$('#searchbox').val(); if(OC.search.lastQuery!==query){ OC.search.lastQuery=query; OC.search.currentResult=-1; + if (FileList && typeof FileList.filter === 'function') { //TODO add hook system + FileList.filter(query); + } if(query.length>2){ OC.search(query); }else{ @@ -840,6 +846,13 @@ function formatDate(date){ return $.datepicker.formatDate(datepickerFormatDate, date)+' '+date.getHours()+':'+((date.getMinutes()<10)?'0':'')+date.getMinutes(); } +// taken from http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery +function getURLParameter(name) { + return decodeURI( + (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, null])[1] + ); +} + /** * takes an absolute timestamp and return a string with a human-friendly relative date * @param int a Unix timestamp diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php index 4d88c2a87f1..9bd50931517 100644 --- a/lib/search/provider/file.php +++ b/lib/search/provider/file.php @@ -10,6 +10,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{ $mime = $fileData['mimetype']; $name = basename($path); + $container = dirname($path); $text = ''; $skip = false; if($mime=='httpd/unix-directory') { @@ -37,7 +38,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{ } } if(!$skip) { - $results[] = new OC_Search_Result($name, $text, $link, $type); + $results[] = new OC_Search_Result($name, $text, $link, $type, $container); } } return $results; diff --git a/lib/search/result.php b/lib/search/result.php index 08beaea151c..42275c2df11 100644 --- a/lib/search/result.php +++ b/lib/search/result.php @@ -7,6 +7,7 @@ class OC_Search_Result{ public $text; public $link; public $type; + public $container; /** * create a new search result @@ -15,10 +16,11 @@ class OC_Search_Result{ * @param string $link link for the result * @param string $type the type of result as human readable string ('File', 'Music', etc) */ - public function __construct($name, $text, $link, $type) { + public function __construct($name, $text, $link, $type, $container) { $this->name=$name; $this->text=$text; $this->link=$link; $this->type=$type; + $this->container=$container; } } diff --git a/search/css/results.css b/search/css/results.css index 4ae7d67afb3..8a32b0b995d 100644 --- a/search/css/results.css +++ b/search/css/results.css @@ -14,7 +14,7 @@ position:fixed; right:0; text-overflow:ellipsis; - top:20px; + top:45px; width:380px; z-index:75; } @@ -43,10 +43,16 @@ } #searchresults td { - vertical-align:top; padding:0 .3em; + height: 32px; +} +#searchresults tr.template { + display: none; } +#searchresults td.result { + width:250px; +} #searchresults td.result div.text { padding-left:1em; white-space:nowrap; @@ -56,6 +62,18 @@ cursor:pointer; } +#searchresults td.container { + width:20px; +} + +#searchresults td.container img { + vertical-align: middle; + display:none; +} +#searchresults tr:hover td.container img { + display:inline; +} + #searchresults td.type { border-bottom:none; border-right:1px solid #aaa; diff --git a/search/js/result.js b/search/js/result.js index 78fa8efc8e9..78d9149f220 100644 --- a/search/js/result.js +++ b/search/js/result.js @@ -8,15 +8,23 @@ OC.search.catagorizeResults=function(results){ types[type].push(results[i]); } return types; -} +}; OC.search.hide=function(){ $('#searchresults').hide(); if($('#searchbox').val().length>2){ $('#searchbox').val(''); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }; -} + if ($('#searchbox').val().length === 0) { + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + } +}; OC.search.showResults=function(results){ - if(results.length==0){ + if(results.length === 0){ return; } if(!OC.search.showResults.loaded){ @@ -30,6 +38,9 @@ OC.search.showResults=function(results){ }); $(document).click(function(event){ OC.search.hide(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }); OC.search.lastResults=results; OC.search.showResults(results); @@ -46,12 +57,27 @@ OC.search.showResults=function(results){ var row=$('#searchresults tr.template').clone(); row.removeClass('template'); row.addClass('result'); - if (i == 0){ + if (i === 0){ row.children('td.type').text(name); } row.find('td.result a').attr('href',type[i].link); row.find('td.result div.name').text(type[i].name); row.find('td.result div.text').text(type[i].text); + if (type[i].container) { + var td = row.find('td.container'); + td.append(''); + td.find('img').attr('src',OC.imagePath('core','places/folder')); + var containerName = OC.basename(type[i].container); + if (containerName === '') { + containerName = '/'; + } + var containerLink = OC.linkTo('files','index.php') + +'?dir='+encodeURIComponent(type[i].container) + +'&scrollto='+encodeURIComponent(type[i].name); + row.find('td.container a') + .attr('href',containerLink) + .attr('title',t('core','Show in {folder}',{folder: containerName})); + } row.data('index',index); index++; if(OC.search.customResults[name]){//give plugins the ability to customize the entries in here @@ -62,7 +88,7 @@ OC.search.showResults=function(results){ } } } -} +}; OC.search.showResults.loaded=false; OC.search.renderCurrent=function(){ @@ -71,4 +97,4 @@ OC.search.renderCurrent=function(){ $('#searchresults tr.result').removeClass('current'); $(result).addClass('current'); } -} +}; diff --git a/search/templates/part.results.php b/search/templates/part.results.php index 9e39a1c2c8b..1469e3468d3 100644 --- a/search/templates/part.results.php +++ b/search/templates/part.results.php @@ -1,7 +1,7 @@
- + +
@@ -9,6 +9,7 @@
-- cgit v1.2.3 From 0d81a53e12bed66e5ec9684424519913283110a5 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 10:00:42 +0200 Subject: use 96x96 as 64x64 thumbnails in conflicts dialog, 64x64 looks very blocky ... maybe something is wrong there --- apps/files/js/files.js | 12 ++++++++---- core/js/oc-dialogs.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index afbb14c5e00..76f19b87cbc 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -627,11 +627,15 @@ function getPathForPreview(name) { return path; } -function lazyLoadPreview(path, mime, ready) { +function lazyLoadPreview(path, mime, ready, width, height) { getMimeIcon(mime,ready); - var x = $('#filestable').data('preview-x'); - var y = $('#filestable').data('preview-y'); - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y}); + if (!width) { + width = $('#filestable').data('preview-x'); + } + if (!height) { + height = $('#filestable').data('preview-y'); + } + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); $.get(previewURL, function() { previewURL = previewURL.replace('(','%28'); previewURL = previewURL.replace(')','%29'); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 7ca94dcbaa4..d661a871a5f 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -296,7 +296,7 @@ var OCdialogs = { var path = getPathForPreview(original.name); lazyLoadPreview(path, original.type, function(previewpath){ conflict.find('.original .icon').css('background-image','url('+previewpath+')'); - }); + }, 96, 96); getCroppedPreview(replacement).then( function(path){ conflict.find('.replacement .icon').css('background-image','url(' + path + ')'); -- cgit v1.2.3 From c30c153ea517403ee479be739a503bd91bab272e Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 19 Sep 2013 11:13:11 +0200 Subject: fixing typos and l10n --- apps/files/js/file-upload.js | 2 +- apps/files/js/files.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8e9bcb885f6..2c42f29445f 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -169,7 +169,7 @@ OC.Upload = { }, /** * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose - * skip all, replace all or choosw which files to keep + * skip all, replace all or choose which files to keep * @param array selection of files to upload * @param callbacks to call: * onNoConflicts, diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 76f19b87cbc..ccb40e7216f 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -499,7 +499,7 @@ var folderDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); @@ -537,7 +537,7 @@ var crumbDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); -- cgit v1.2.3 From 98ff8478301676c99ffefd5756ad22466dfb6acf Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 14:46:33 +0200 Subject: fix race condition in lazy preview loading --- apps/files/js/files.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index ccb40e7216f..5ec65d87457 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -628,18 +628,24 @@ function getPathForPreview(name) { } function lazyLoadPreview(path, mime, ready, width, height) { - getMimeIcon(mime,ready); - if (!width) { - width = $('#filestable').data('preview-x'); - } - if (!height) { - height = $('#filestable').data('preview-y'); - } - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); - $.get(previewURL, function() { - previewURL = previewURL.replace('(','%28'); - previewURL = previewURL.replace(')','%29'); - ready(previewURL + '&reload=true'); + // get mime icon url + getMimeIcon(mime, function(iconURL) { + ready(iconURL); // set mimeicon URL + + // now try getting a preview thumbnail URL + if ( ! width ) { + width = $('#filestable').data('preview-x'); + } + if ( ! height ) { + height = $('#filestable').data('preview-y'); + } + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + $.get(previewURL, function() { + previewURL = previewURL.replace('(', '%28'); + previewURL = previewURL.replace(')', '%29'); + //set preview thumbnail URL + ready(previewURL + '&reload=true'); + }); }); } -- cgit v1.2.3 From 8a1618bce56a32e311626cd9f0e322dd7cf330c4 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 23 Sep 2013 12:27:05 +0200 Subject: implement previews for public upload --- apps/files/js/files.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 8ccb448abfb..ec688eaf63e 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -644,7 +644,11 @@ function lazyLoadPreview(path, mime, ready, width, height) { if ( ! height ) { height = $('#filestable').data('preview-y'); } - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + if( $('#publicUploadButtonMock').length ) { + var previewURL = OC.Router.generate('core_ajax_public_preview', {file: encodeURIComponent(path), x:width, y:height, t:$('#dirToken').val()}); + } else { + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + } $.get(previewURL, function() { previewURL = previewURL.replace('(', '%28'); previewURL = previewURL.replace(')', '%29'); -- cgit v1.2.3