diff options
Diffstat (limited to 'apps/files/js')
-rw-r--r-- | apps/files/js/detailsview.js | 2 | ||||
-rw-r--r-- | apps/files/js/detailtabview.js | 7 | ||||
-rw-r--r-- | apps/files/js/file-upload.js | 149 | ||||
-rw-r--r-- | apps/files/js/fileactionsmenu.js | 4 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 192 | ||||
-rw-r--r-- | apps/files/js/mainfileinfodetailview.js | 6 | ||||
-rw-r--r-- | apps/files/js/newfilemenu.js | 235 | ||||
-rw-r--r-- | apps/files/js/tagsplugin.js | 16 |
8 files changed, 449 insertions, 162 deletions
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 83d7fd4a178..3a775c29ec6 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -35,7 +35,7 @@ var DetailsView = OC.Backbone.View.extend({ id: 'app-sidebar', tabName: 'div', - className: 'detailsView', + className: 'detailsView scroll-container', _template: null, diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js index b0e170bc4e7..449047cf252 100644 --- a/apps/files/js/detailtabview.js +++ b/apps/files/js/detailtabview.js @@ -84,6 +84,13 @@ */ getFileInfo: function() { return this.model; + }, + + /** + * Load the next page of results + */ + nextPage: function() { + // load the next page, if applicable } }); DetailTabView._TAB_COUNT = 0; diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 6b6acdb5e01..17f0f777169 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -551,155 +551,6 @@ OC.Upload = { $('#file_upload_start').attr('multiple', 'multiple'); } - $(document).click(function(ev) { - // do not close when clicking in the dropdown - if ($(ev.target).closest('#new').length){ - return; - } - $('#new>ul').hide(); - $('#new').removeClass('active'); - if ($('#new .error').length > 0) { - $('#new .error').tipsy('hide'); - } - $('#new li').each(function(i,element) { - if ($(element).children('p').length === 0) { - $(element).children('form').remove(); - $(element).append('<p>' + $(element).data('text') + '</p>'); - } - }); - }); - $('#new').click(function(event) { - event.stopPropagation(); - }); - $('#new>a').click(function() { - $('#new>ul').toggle(); - $('#new').toggleClass('active'); - }); - $('#new li').click(function() { - if ($(this).children('p').length === 0) { - return; - } - - $('#new .error').tipsy('hide'); - - $('#new li').each(function(i, element) { - if ($(element).children('p').length === 0) { - $(element).children('form').remove(); - $(element).append('<p>' + $(element).data('text') + '</p>'); - } - }); - - var type = $(this).data('type'); - var text = $(this).children('p').text(); - $(this).data('text', text); - $(this).children('p').remove(); - - // add input field - var form = $('<form></form>'); - var input = $('<input type="text">'); - var newName = $(this).attr('data-newname') || ''; - var fileType = 'input-' + $(this).attr('data-type'); - if (newName) { - input.val(newName); - input.attr('id', fileType); - } - var label = $('<label class="hidden-visually" for="">' + escapeHTML(newName) + '</label>'); - label.attr('for', fileType); - - form.append(label).append(input); - $(this).append(form); - var lastPos; - var checkInput = function () { - var filename = input.val(); - if (!Files.isFileNameValid(filename)) { - // Files.isFileNameValid(filename) throws an exception itself - } else if (FileList.inList(filename)) { - throw t('files', '{new_name} already exists', {new_name: filename}); - } else { - return true; - } - }; - - // verify filename on typing - input.keyup(function(event) { - try { - checkInput(); - input.tipsy('hide'); - input.removeClass('error'); - } catch (error) { - input.attr('title', error); - input.tipsy({gravity: 'w', trigger: 'manual'}); - input.tipsy('show'); - input.addClass('error'); - } - }); - - input.focus(); - // pre select name up to the extension - lastPos = newName.lastIndexOf('.'); - if (lastPos === -1) { - lastPos = newName.length; - } - input.selectRange(0, lastPos); - form.submit(function(event) { - event.stopPropagation(); - event.preventDefault(); - try { - checkInput(); - var newname = input.val(); - if (FileList.lastAction) { - FileList.lastAction(); - } - var name = FileList.getUniqueName(newname); - switch(type) { - case 'file': - $.post( - OC.filePath('files', 'ajax', 'newfile.php'), - { - dir: FileList.getCurrentDirectory(), - filename: name - }, - function(result) { - if (result.status === 'success') { - FileList.add(result.data, {animate: true, scrollTo: true}); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Could not create file')); - } - } - ); - break; - case 'folder': - $.post( - OC.filePath('files','ajax','newfolder.php'), - { - dir: FileList.getCurrentDirectory(), - foldername: name - }, - function(result) { - if (result.status === 'success') { - FileList.add(result.data, {animate: true, scrollTo: true}); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); - } - } - ); - break; - } - var li = form.parent(); - form.remove(); - /* workaround for IE 9&10 click event trap, 2 lines: */ - $('input').first().focus(); - $('#content').focus(); - li.append('<p>' + li.data('text') + '</p>'); - $('#new>a').click(); - } catch (error) { - input.attr('title', error); - input.tipsy({gravity: 'w', trigger: 'manual'}); - input.tipsy('show'); - input.addClass('error'); - } - }); - }); window.file_upload_param = file_upload_param; return file_upload_param; } diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js index 623ebde5442..5ab0e42f93e 100644 --- a/apps/files/js/fileactionsmenu.js +++ b/apps/files/js/fileactionsmenu.js @@ -14,7 +14,7 @@ '<ul>' + '{{#each items}}' + '<li>' + - '<a href="#" class="action action-{{nameLowerCase}} permanent" data-action="{{name}}">{{#if icon}}<img src="{{icon}}"/>{{else}}<span class="no-icon"></span>{{/if}}<span>{{displayName}}</span></a>' + + '<a href="#" class="menuitem action action-{{nameLowerCase}} permanent" data-action="{{name}}">{{#if icon}}<img class="icon" src="{{icon}}"/>{{else}}<span class="no-icon"></span>{{/if}}<span>{{displayName}}</span></a>' + '</li>' + '{{/each}}' + '</ul>'; @@ -26,7 +26,7 @@ */ var FileActionsMenu = OC.Backbone.View.extend({ tagName: 'div', - className: 'fileActionsMenu bubble hidden open menu', + className: 'fileActionsMenu popovermenu bubble hidden open menu', /** * Current context diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 9593ee79e66..c52e414e3a7 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -9,6 +9,9 @@ */ (function() { + + var TEMPLATE_ADDBUTTON = '<a href="#" class="button new" title="{{addText}}"><img src="{{iconUrl}}"></img></a>'; + /** * @class OCA.Files.FileList * @classdesc @@ -220,6 +223,8 @@ this.$el.find('#controls').prepend(this.breadcrumb.$el); + this._renderNewButton(); + this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this)); this._onResize = _.debounce(_.bind(this._onResize, this), 100); @@ -262,6 +267,12 @@ * Destroy / uninitialize this instance. */ destroy: function() { + if (this._newFileMenu) { + this._newFileMenu.remove(); + } + if (this._newButton) { + this._newButton.remove(); + } // TODO: also unregister other event handlers this.fileActions.off('registerAction', this._onFileActionsUpdated); this.fileActions.off('setDefault', this._onFileActionsUpdated); @@ -291,6 +302,7 @@ * @return {OCA.Files.FileInfoModel} file info model */ getModelForFile: function(fileName) { + var self = this; var $tr; // jQuery object ? if (fileName.is) { @@ -318,6 +330,21 @@ if (!model.has('path')) { model.set('path', this.getCurrentDirectory(), {silent: true}); } + + model.on('change', function(model) { + // re-render row + var highlightState = $tr.hasClass('highlighted'); + $tr = self.updateRow( + $tr, + _.extend({isPreviewAvailable: true}, model.toJSON()), + {updateSummary: true, silent: false, animate: true} + ); + $tr.toggleClass('highlighted', highlightState); + }); + model.on('busy', function(model, state) { + self.showFileBusyState($tr, state); + }); + return model; }, @@ -341,6 +368,9 @@ if (!fileName) { OC.Apps.hideAppSidebar(this._detailsView.$el); this._detailsView.setFileInfo(null); + if (this._currentFileModel) { + this._currentFileModel.off(); + } this._currentFileModel = null; return; } @@ -1223,6 +1253,10 @@ reload: function() { this._selectedFiles = {}; this._selectionSummary.clear(); + if (this._currentFileModel) { + this._currentFileModel.off(); + } + this._currentFileModel = null; this.$el.find('.select-all').prop('checked', false); this.showMask(); if (this._reloadCall) { @@ -1555,6 +1589,23 @@ }, /** + * Updates the given row with the given file info + * + * @param {Object} $tr row element + * @param {OCA.Files.FileInfo} fileInfo file info + * @param {Object} options options + * + * @return {Object} new row element + */ + updateRow: function($tr, fileInfo, options) { + this.files.splice($tr.index(), 1); + $tr.remove(); + $tr = this.add(fileInfo, _.extend({updateSummary: false, silent: true}, options)); + this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: $tr})); + return $tr; + }, + + /** * Triggers file rename input field for the given file name. * If the user enters a new name, the file will be renamed. * @@ -1690,6 +1741,106 @@ form.trigger('submit'); }); }, + + /** + * Create an empty file inside the current directory. + * + * @param {string} name name of the file + * + * @return {Promise} promise that will be resolved after the + * file was created + * + * @since 8.2 + */ + createFile: function(name) { + var self = this; + var deferred = $.Deferred(); + var promise = deferred.promise(); + + OCA.Files.Files.isFileNameValid(name); + name = this.getUniqueName(name); + + if (this.lastAction) { + this.lastAction(); + } + + $.post( + OC.generateUrl('/apps/files/ajax/newfile.php'), + { + dir: this.getCurrentDirectory(), + filename: name + }, + function(result) { + if (result.status === 'success') { + self.add(result.data, {animate: true, scrollTo: true}); + deferred.resolve(result.status, result.data); + } else { + if (result.data && result.data.message) { + OC.Notification.showTemporary(result.data.message); + } else { + OC.Notification.showTemporary(t('core', 'Could not create file')); + } + deferred.reject(result.status, result.data); + } + } + ); + + return promise; + }, + + /** + * Create a directory inside the current directory. + * + * @param {string} name name of the directory + * + * @return {Promise} promise that will be resolved after the + * directory was created + * + * @since 8.2 + */ + createDirectory: function(name) { + var self = this; + var deferred = $.Deferred(); + var promise = deferred.promise(); + + OCA.Files.Files.isFileNameValid(name); + name = this.getUniqueName(name); + + if (this.lastAction) { + this.lastAction(); + } + + $.post( + OC.generateUrl('/apps/files/ajax/newfolder.php'), + { + dir: this.getCurrentDirectory(), + foldername: name + }, + function(result) { + if (result.status === 'success') { + self.add(result.data, {animate: true, scrollTo: true}); + deferred.resolve(result.status, result.data); + } else { + if (result.data && result.data.message) { + OC.Notification.showTemporary(result.data.message); + } else { + OC.Notification.showTemporary(t('core', 'Could not create folder')); + } + deferred.reject(result.status); + } + } + ); + + return promise; + }, + + /** + * Returns whether the given file name exists in the list + * + * @param {string} file file name + * + * @return {bool} true if the file exists in the list, false otherwise + */ inList:function(file) { return this.findFileEl(file).length; }, @@ -2351,6 +2502,47 @@ }); }, + _renderNewButton: function() { + // if an upload button (legacy) already exists, skip + if ($('#controls .button.upload').length) { + return; + } + if (!this._addButtonTemplate) { + this._addButtonTemplate = Handlebars.compile(TEMPLATE_ADDBUTTON); + } + var $newButton = $(this._addButtonTemplate({ + addText: t('files', 'New'), + iconUrl: OC.imagePath('core', 'actions/add') + })); + + $('#controls .actions').prepend($newButton); + $newButton.tooltip({'placement': 'bottom'}); + + $newButton.click(_.bind(this._onClickNewButton, this)); + this._newButton = $newButton; + }, + + _onClickNewButton: function(event) { + var $target = $(event.target); + if (!$target.hasClass('.button')) { + $target = $target.closest('.button'); + } + this._newButton.tooltip('hide'); + event.preventDefault(); + if ($target.hasClass('disabled')) { + return false; + } + if (!this._newFileMenu) { + this._newFileMenu = new OCA.Files.NewFileMenu({ + fileList: this + }); + $('body').append(this._newFileMenu.$el); + } + this._newFileMenu.showAt($target); + + return false; + }, + /** * Register a tab view to be added to all views */ diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 785eed8d712..efdbb5e2ad1 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -124,7 +124,7 @@ // TODO: we really need OC.Previews var $iconDiv = this.$el.find('.thumbnail'); - $iconDiv.addClass('icon-loading'); + $iconDiv.addClass('icon-loading icon-32'); $container = this.$el.find('.thumbnailContainer'); if (!this.model.isDirectory()) { this._fileList.lazyLoadPreview({ @@ -137,7 +137,7 @@ callback: function(previewUrl, img) { $iconDiv.previewImg = previewUrl; if (img) { - $iconDiv.removeClass('icon-loading'); + $iconDiv.removeClass('icon-loading icon-32'); if(img.height > img.width) { $container.addClass('portrait'); } @@ -163,7 +163,7 @@ } }.bind(this), error: function() { - $iconDiv.removeClass('icon-loading'); + $iconDiv.removeClass('icon-loading icon-32'); this.$el.find('.thumbnailContainer').removeClass('image'); //fall back to regular view $iconDiv.css({ 'background-image': 'url("' + $iconDiv.previewImg + '")' diff --git a/apps/files/js/newfilemenu.js b/apps/files/js/newfilemenu.js new file mode 100644 index 00000000000..4c021e6b873 --- /dev/null +++ b/apps/files/js/newfilemenu.js @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global Files */ + +(function() { + + var TEMPLATE_MENU = + '<ul>' + + '<li>' + + '<label for="file_upload_start" class="menuitem" data-action="upload" title="{{uploadMaxHumanFilesize}}"><span class="svg icon icon-upload"></span><span class="displayname">{{uploadLabel}}</span></label>' + + '</li>' + + '{{#each items}}' + + '<li>' + + '<a href="#" class="menuitem" data-templatename="{{templateName}}" data-filetype="{{fileType}}" data-action="{{id}}"><span class="icon {{iconClass}} svg"></span><span class="displayname">{{displayName}}</span></a>' + + '</li>' + + '{{/each}}' + + '</ul>'; + + var TEMPLATE_FILENAME_FORM = + '<form class="filenameform">' + + '<label class="hidden-visually" for="{{cid}}-input-{{fileType}}">{{fileName}}</label>' + + '<input id="{{cid}}-input-{{fileType}}" type="text" value="{{fileName}}">' + + '</form>'; + + /** + * Construct a new NewFileMenu instance + * @constructs NewFileMenu + * + * @memberof OCA.Files + */ + var NewFileMenu = OC.Backbone.View.extend({ + tagName: 'div', + className: 'newFileMenu popovermenu bubble hidden open menu', + + events: { + 'click .menuitem': '_onClickAction' + }, + + initialize: function(options) { + var self = this; + var $uploadEl = $('#file_upload_start'); + if ($uploadEl.length) { + $uploadEl.on('fileuploadstart', function() { + self.trigger('actionPerformed', 'upload'); + }); + } else { + console.warn('Missing upload element "file_upload_start"'); + } + + this._fileList = options && options.fileList; + }, + + template: function(data) { + if (!OCA.Files.NewFileMenu._TEMPLATE) { + OCA.Files.NewFileMenu._TEMPLATE = Handlebars.compile(TEMPLATE_MENU); + } + return OCA.Files.NewFileMenu._TEMPLATE(data); + }, + + /** + * Event handler whenever an action has been clicked within the menu + * + * @param {Object} event event object + */ + _onClickAction: function(event) { + var $target = $(event.target); + if (!$target.hasClass('menuitem')) { + $target = $target.closest('.menuitem'); + } + var action = $target.attr('data-action'); + // note: clicking the upload label will automatically + // set the focus on the "file_upload_start" hidden field + // which itself triggers the upload dialog. + // Currently the upload logic is still in file-upload.js and filelist.js + if (action === 'upload') { + OC.hideMenus(); + } else { + event.preventDefault(); + this._promptFileName($target); + } + }, + + _promptFileName: function($target) { + var self = this; + if (!OCA.Files.NewFileMenu._TEMPLATE_FORM) { + OCA.Files.NewFileMenu._TEMPLATE_FORM = Handlebars.compile(TEMPLATE_FILENAME_FORM); + } + + if ($target.find('form').length) { + $target.find('input').focus(); + return; + } + + // discard other forms + this.$el.find('form').remove(); + this.$el.find('.displayname').removeClass('hidden'); + + $target.find('.displayname').addClass('hidden'); + + var newName = $target.attr('data-templatename'); + var fileType = $target.attr('data-filetype'); + var $form = $(OCA.Files.NewFileMenu._TEMPLATE_FORM({ + fileName: newName, + cid: this.cid, + fileType: fileType + })); + + //this.trigger('actionPerformed', action); + $target.append($form); + + // here comes the OLD code + var $input = $form.find('input'); + + var lastPos; + var checkInput = function () { + var filename = $input.val(); + try { + if (!Files.isFileNameValid(filename)) { + // Files.isFileNameValid(filename) throws an exception itself + } else if (self._fileList.inList(filename)) { + throw t('files', '{newname} already exists', {newname: filename}); + } else { + return true; + } + } catch (error) { + $input.attr('title', error); + $input.tooltip({placement: 'right', trigger: 'manual'}); + $input.tooltip('show'); + $input.addClass('error'); + } + return false; + }; + + // verify filename on typing + $input.keyup(function() { + if (checkInput()) { + $input.tooltip('hide'); + $input.removeClass('error'); + } + }); + + $input.focus(); + // pre select name up to the extension + lastPos = newName.lastIndexOf('.'); + if (lastPos === -1) { + lastPos = newName.length; + } + $input.selectRange(0, lastPos); + + $form.submit(function(event) { + event.stopPropagation(); + event.preventDefault(); + + if (checkInput()) { + var newname = $input.val(); + self._createFile(fileType, newname); + $form.remove(); + $target.find('.displayname').removeClass('hidden'); + OC.hideMenus(); + } + }); + }, + + /** + * Creates a file with the given type and name. + * This calls the matching methods on the attached file list. + * + * @param {string} fileType file type + * @param {string} name file name + */ + _createFile: function(fileType, name) { + switch(fileType) { + case 'file': + this._fileList.createFile(name); + break; + case 'folder': + this._fileList.createDirectory(name); + break; + default: + console.warn('Unknown file type "' + fileType + '"'); + } + }, + + /** + * Renders the menu with the currently set items + */ + render: function() { + this.$el.html(this.template({ + uploadMaxHumanFileSize: 'TODO', + uploadLabel: t('files', 'Upload'), + items: [{ + id: 'file', + displayName: t('files', 'Text file'), + templateName: t('files', 'New text file.txt'), + iconClass: 'icon-filetype-text', + fileType: 'file' + }, { + id: 'folder', + displayName: t('files', 'Folder'), + templateName: t('files', 'New folder'), + iconClass: 'icon-folder', + fileType: 'folder' + }] + })); + }, + + /** + * Displays the menu under the given element + * + * @param {Object} $target target element + */ + showAt: function($target) { + this.render(); + var targetOffset = $target.offset(); + this.$el.css({ + left: targetOffset.left, + top: targetOffset.top + $target.height() + }); + this.$el.removeClass('hidden'); + + OC.showMenu(null, this.$el); + } + }); + + OCA.Files.NewFileMenu = NewFileMenu; + +})(); diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js index 609e38ca9a9..9f45da9a6e2 100644 --- a/apps/files/js/tagsplugin.js +++ b/apps/files/js/tagsplugin.js @@ -92,6 +92,7 @@ actionHandler: function(fileName, context) { var $actionEl = context.$file.find('.action-favorite'); var $file = context.$file; + var fileInfo = context.fileList.files[$file.index()]; var dir = context.dir || context.fileList.getCurrentDirectory(); var tags = $file.attr('data-tags'); if (_.isUndefined(tags)) { @@ -106,9 +107,11 @@ } else { tags.push(OC.TAG_FAVORITE); } + + // pre-toggle the star toggleStar($actionEl, !isFavorite); - context.fileInfoModel.set('tags', tags); + context.fileInfoModel.trigger('busy', context.fileInfoModel, true); self.applyFileTags( dir + '/' + fileName, @@ -116,17 +119,16 @@ $actionEl, isFavorite ).then(function(result) { + context.fileInfoModel.trigger('busy', context.fileInfoModel, false); // response from server should contain updated tags var newTags = result.tags; if (_.isUndefined(newTags)) { newTags = tags; } - var fileInfo = context.fileList.files[$file.index()]; - // read latest state from result - toggleStar($actionEl, (newTags.indexOf(OC.TAG_FAVORITE) >= 0)); - $file.attr('data-tags', newTags.join('|')); - $file.attr('data-favorite', !isFavorite); - fileInfo.tags = newTags; + context.fileInfoModel.set({ + 'tags': newTags, + 'favorite': !isFavorite + }); }); } }); |