Infinite scrolling for files apptags/v7.0.0alpha2
@@ -310,7 +310,6 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } | |||
/* Actions for selected files */ | |||
.selectedActions { | |||
display: none; | |||
position: absolute; | |||
top: -1px; | |||
right: 0; |
@@ -32,6 +32,7 @@ OCP\Util::addscript('files', 'file-upload'); | |||
OCP\Util::addscript('files', 'jquery.iframe-transport'); | |||
OCP\Util::addscript('files', 'jquery.fileupload'); | |||
OCP\Util::addscript('files', 'jquery-visibility'); | |||
OCP\Util::addscript('files', 'filesummary'); | |||
OCP\Util::addscript('files', 'breadcrumb'); | |||
OCP\Util::addscript('files', 'filelist'); | |||
@@ -606,7 +606,7 @@ OC.Upload = { | |||
{dir:$('#dir').val(), filename:name}, | |||
function(result) { | |||
if (result.status === 'success') { | |||
FileList.add(result.data, {hidden: hidden, insert: true}); | |||
FileList.add(result.data, {hidden: hidden, animate: true}); | |||
} else { | |||
OC.dialogs.alert(result.data.message, t('core', 'Could not create file')); | |||
} | |||
@@ -619,7 +619,7 @@ OC.Upload = { | |||
{dir:$('#dir').val(), foldername:name}, | |||
function(result) { | |||
if (result.status === 'success') { | |||
FileList.add(result.data, {hidden: hidden, insert: true}); | |||
FileList.add(result.data, {hidden: hidden, animate: true}); | |||
} else { | |||
OC.dialogs.alert(result.data.message, t('core', 'Could not create folder')); | |||
} | |||
@@ -657,7 +657,7 @@ OC.Upload = { | |||
var file = data; | |||
$('#uploadprogressbar').fadeOut(); | |||
FileList.add(file, {hidden: hidden, insert: true}); | |||
FileList.add(file, {hidden: hidden, animate: true}); | |||
}); | |||
eventSource.listen('error',function(error) { | |||
$('#uploadprogressbar').fadeOut(); |
@@ -8,8 +8,8 @@ | |||
* | |||
*/ | |||
/* global OC, t, n, FileList, FileActions */ | |||
/* global getURLParameter, isPublic */ | |||
/* global OC, t, FileList */ | |||
/* global getURLParameter */ | |||
var Files = { | |||
// file space size sync | |||
_updateStorageStatistics: function() { | |||
@@ -96,10 +96,10 @@ var Files = { | |||
throw t('files', 'File name cannot be empty.'); | |||
} | |||
// check for invalid characters | |||
var invalid_characters = | |||
var invalidCharacters = | |||
['\\', '/', '<', '>', ':', '"', '|', '?', '*', '\n']; | |||
for (var i = 0; i < invalid_characters.length; i++) { | |||
if (trimmedName.indexOf(invalid_characters[i]) !== -1) { | |||
for (var i = 0; i < invalidCharacters.length; i++) { | |||
if (trimmedName.indexOf(invalidCharacters[i]) !== -1) { | |||
throw t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed."); | |||
} | |||
} | |||
@@ -116,7 +116,8 @@ var Files = { | |||
return; | |||
} | |||
if (usedSpacePercent > 90) { | |||
OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)', {usedSpacePercent: usedSpacePercent})); | |||
OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)', | |||
{usedSpacePercent: usedSpacePercent})); | |||
} | |||
}, | |||
@@ -142,6 +143,7 @@ var Files = { | |||
} | |||
}, | |||
// TODO: move to FileList class | |||
setupDragAndDrop: function() { | |||
var $fileList = $('#fileList'); | |||
@@ -209,7 +211,7 @@ $(document).ready(function() { | |||
// Trigger cancelling of file upload | |||
$('#uploadprogresswrapper .stop').on('click', function() { | |||
OC.Upload.cancelUploads(); | |||
procesSelection(); | |||
FileList.updateSelectionSummary(); | |||
}); | |||
// Show trash bin | |||
@@ -217,135 +219,11 @@ $(document).ready(function() { | |||
window.location=OC.filePath('files_trashbin', '', 'index.php'); | |||
}); | |||
var lastChecked; | |||
// Sets the file link behaviour : | |||
$('#fileList').on('click','td.filename a',function(event) { | |||
if (event.ctrlKey || event.shiftKey) { | |||
event.preventDefault(); | |||
if (event.shiftKey) { | |||
var last = $(lastChecked).parent().parent().prevAll().length; | |||
var first = $(this).parent().parent().prevAll().length; | |||
var start = Math.min(first, last); | |||
var end = Math.max(first, last); | |||
var rows = $(this).parent().parent().parent().children('tr'); | |||
for (var i = start; i < end; i++) { | |||
$(rows).each(function(index) { | |||
if (index === i) { | |||
var checkbox = $(this).children().children('input:checkbox'); | |||
$(checkbox).attr('checked', 'checked'); | |||
$(checkbox).parent().parent().addClass('selected'); | |||
} | |||
}); | |||
} | |||
} | |||
var checkbox = $(this).parent().children('input:checkbox'); | |||
lastChecked = checkbox; | |||
if ($(checkbox).attr('checked')) { | |||
$(checkbox).removeAttr('checked'); | |||
$(checkbox).parent().parent().removeClass('selected'); | |||
$('#select_all').removeAttr('checked'); | |||
} else { | |||
$(checkbox).attr('checked', 'checked'); | |||
$(checkbox).parent().parent().toggleClass('selected'); | |||
var selectedCount = $('td.filename input:checkbox:checked').length; | |||
if (selectedCount === $('td.filename input:checkbox').length) { | |||
$('#select_all').attr('checked', 'checked'); | |||
} | |||
} | |||
procesSelection(); | |||
} else { | |||
var filename=$(this).parent().parent().attr('data-file'); | |||
var tr = FileList.findFileEl(filename); | |||
var renaming=tr.data('renaming'); | |||
if (!renaming) { | |||
FileActions.currentFile = $(this).parent(); | |||
var mime=FileActions.getCurrentMimeType(); | |||
var type=FileActions.getCurrentType(); | |||
var permissions = FileActions.getCurrentPermissions(); | |||
var action=FileActions.getDefault(mime,type, permissions); | |||
if (action) { | |||
event.preventDefault(); | |||
action(filename); | |||
} | |||
} | |||
} | |||
}); | |||
// Sets the select_all checkbox behaviour : | |||
$('#select_all').click(function() { | |||
if ($(this).attr('checked')) { | |||
// Check all | |||
$('td.filename input:checkbox').attr('checked', true); | |||
$('td.filename input:checkbox').parent().parent().addClass('selected'); | |||
} else { | |||
// Uncheck all | |||
$('td.filename input:checkbox').attr('checked', false); | |||
$('td.filename input:checkbox').parent().parent().removeClass('selected'); | |||
} | |||
procesSelection(); | |||
}); | |||
$('#fileList').on('change', 'td.filename input:checkbox',function(event) { | |||
if (event.shiftKey) { | |||
var last = $(lastChecked).parent().parent().prevAll().length; | |||
var first = $(this).parent().parent().prevAll().length; | |||
var start = Math.min(first, last); | |||
var end = Math.max(first, last); | |||
var rows = $(this).parent().parent().parent().children('tr'); | |||
for (var i = start; i < end; i++) { | |||
$(rows).each(function(index) { | |||
if (index === i) { | |||
var checkbox = $(this).children().children('input:checkbox'); | |||
$(checkbox).attr('checked', 'checked'); | |||
$(checkbox).parent().parent().addClass('selected'); | |||
} | |||
}); | |||
} | |||
} | |||
var selectedCount=$('td.filename input:checkbox:checked').length; | |||
$(this).parent().parent().toggleClass('selected'); | |||
if (!$(this).attr('checked')) { | |||
$('#select_all').attr('checked',false); | |||
} else { | |||
if (selectedCount===$('td.filename input:checkbox').length) { | |||
$('#select_all').attr('checked',true); | |||
} | |||
} | |||
procesSelection(); | |||
}); | |||
$('.download').click('click',function(event) { | |||
var files; | |||
var dir = FileList.getCurrentDirectory(); | |||
if (FileList.isAllSelected()) { | |||
files = OC.basename(dir); | |||
dir = OC.dirname(dir) || '/'; | |||
} | |||
else { | |||
files = Files.getSelectedFiles('name'); | |||
} | |||
OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.')); | |||
OC.redirect(Files.getDownloadUrl(files, dir)); | |||
return false; | |||
}); | |||
$('.delete-selected').click(function(event) { | |||
var files = Files.getSelectedFiles('name'); | |||
event.preventDefault(); | |||
if (FileList.isAllSelected()) { | |||
files = null; | |||
} | |||
FileList.do_delete(files); | |||
return false; | |||
}); | |||
// drag&drop support using jquery.fileupload | |||
// TODO use OC.dialogs | |||
$(document).bind('drop dragover', function (e) { | |||
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone | |||
}); | |||
}); | |||
//do a background scan if needed | |||
scanFiles(); | |||
@@ -422,34 +300,22 @@ function scanFiles(force, dir, users) { | |||
} | |||
scanFiles.scanning=false; | |||
function boolOperationFinished(data, callback) { | |||
result = jQuery.parseJSON(data.responseText); | |||
Files.updateMaxUploadFilesize(result); | |||
if (result.status === 'success') { | |||
callback.call(); | |||
} else { | |||
alert(result.data.message); | |||
} | |||
} | |||
// TODO: move to FileList | |||
var createDragShadow = function(event) { | |||
//select dragged file | |||
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); | |||
if (!isDragSelected) { | |||
//select dragged file | |||
$(event.target).parents('tr').find('td input:first').prop('checked',true); | |||
FileList._selectFileEl($(event.target).parents('tr:first'), true); | |||
} | |||
var selectedFiles = Files.getSelectedFiles(); | |||
// do not show drag shadow for too many files | |||
var selectedFiles = _.first(FileList.getSelectedFiles(), FileList.pageSize); | |||
selectedFiles.sort(FileList._fileInfoCompare); | |||
if (!isDragSelected && selectedFiles.length === 1) { | |||
//revert the selection | |||
$(event.target).parents('tr').find('td input:first').prop('checked',false); | |||
} | |||
//also update class when we dragged more than one file | |||
if (selectedFiles.length > 1) { | |||
$(event.target).parents('tr').addClass('selected'); | |||
FileList._selectFileEl($(event.target).parents('tr:first'), false); | |||
} | |||
// build dragshadow | |||
@@ -460,9 +326,12 @@ var createDragShadow = function(event) { | |||
var dir=$('#dir').val(); | |||
$(selectedFiles).each(function(i,elem) { | |||
var newtr = $('<tr/>').attr('data-dir', dir).attr('data-filename', elem.name).attr('data-origin', elem.origin); | |||
var newtr = $('<tr/>') | |||
.attr('data-dir', dir) | |||
.attr('data-file', elem.name) | |||
.attr('data-origin', elem.origin); | |||
newtr.append($('<td/>').addClass('filename').text(elem.name)); | |||
newtr.append($('<td/>').addClass('size').text(humanFileSize(elem.size))); | |||
newtr.append($('<td/>').addClass('size').text(OC.Util.humanFileSize(elem.size))); | |||
tbody.append(newtr); | |||
if (elem.type === 'dir') { | |||
newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); | |||
@@ -479,154 +348,57 @@ var createDragShadow = function(event) { | |||
//options for file drag/drop | |||
//start&stop handlers needs some cleaning up | |||
// TODO: move to FileList class | |||
var dragOptions={ | |||
revert: 'invalid', revertDuration: 300, | |||
opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: 24, top: 18 }, | |||
helper: createDragShadow, cursor: 'move', | |||
start: function(event, ui){ | |||
var $selectedFiles = $('td.filename input:checkbox:checked'); | |||
if($selectedFiles.length > 1){ | |||
$selectedFiles.parents('tr').fadeTo(250, 0.2); | |||
} | |||
else{ | |||
$(this).fadeTo(250, 0.2); | |||
} | |||
}, | |||
stop: function(event, ui) { | |||
var $selectedFiles = $('td.filename input:checkbox:checked'); | |||
if($selectedFiles.length > 1){ | |||
$selectedFiles.parents('tr').fadeTo(250, 1); | |||
} | |||
else{ | |||
$(this).fadeTo(250, 1); | |||
} | |||
$('#fileList tr td.filename').addClass('ui-draggable'); | |||
start: function(event, ui){ | |||
var $selectedFiles = $('td.filename input:checkbox:checked'); | |||
if($selectedFiles.length > 1){ | |||
$selectedFiles.parents('tr').fadeTo(250, 0.2); | |||
} | |||
else{ | |||
$(this).fadeTo(250, 0.2); | |||
} | |||
}, | |||
stop: function(event, ui) { | |||
var $selectedFiles = $('td.filename input:checkbox:checked'); | |||
if($selectedFiles.length > 1){ | |||
$selectedFiles.parents('tr').fadeTo(250, 1); | |||
} | |||
else{ | |||
$(this).fadeTo(250, 1); | |||
} | |||
$('#fileList tr td.filename').addClass('ui-draggable'); | |||
} | |||
}; | |||
// sane browsers support using the distance option | |||
if ( $('html.ie').length === 0) { | |||
dragOptions['distance'] = 20; | |||
} | |||
var folderDropOptions={ | |||
// TODO: move to FileList class | |||
var folderDropOptions = { | |||
hoverClass: "canDrop", | |||
drop: function( event, ui ) { | |||
//don't allow moving a file into a selected folder | |||
// don't allow moving a file into a selected folder | |||
if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { | |||
return false; | |||
} | |||
var target = $(this).closest('tr').data('file'); | |||
var files = ui.helper.find('tr'); | |||
$(files).each(function(i,row) { | |||
var dir = $(row).data('dir'); | |||
var file = $(row).data('filename'); | |||
//slapdash selector, tracking down our original element that the clone budded off of. | |||
var origin = $('tr[data-id=' + $(row).data('origin') + ']'); | |||
var td = origin.children('td.filename'); | |||
var oldBackgroundImage = td.css('background-image'); | |||
td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); | |||
$.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { | |||
if (result) { | |||
if (result.status === 'success') { | |||
//recalculate folder size | |||
var oldFile = FileList.findFileEl(target); | |||
var newFile = FileList.findFileEl(file); | |||
var oldSize = oldFile.data('size'); | |||
var newSize = oldSize + newFile.data('size'); | |||
oldFile.data('size', newSize); | |||
oldFile.find('td.filesize').text(humanFileSize(newSize)); | |||
FileList.remove(file); | |||
procesSelection(); | |||
$('#notification').hide(); | |||
} else { | |||
$('#notification').hide(); | |||
$('#notification').text(result.data.message); | |||
$('#notification').fadeIn(); | |||
} | |||
} else { | |||
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); | |||
} | |||
td.css('background-image', oldBackgroundImage); | |||
}); | |||
}); | |||
}, | |||
tolerance: 'pointer' | |||
}; | |||
var targetPath = FileList.getCurrentDirectory() + '/' + $(this).closest('tr').data('file'); | |||
function procesSelection() { | |||
var selected = Files.getSelectedFiles(); | |||
var selectedFiles = selected.filter(function(el) { | |||
return el.type==='file'; | |||
}); | |||
var selectedFolders = selected.filter(function(el) { | |||
return el.type==='dir'; | |||
}); | |||
if (selectedFiles.length === 0 && selectedFolders.length === 0) { | |||
$('#headerName span.name').text(t('files','Name')); | |||
$('#headerSize').text(t('files','Size')); | |||
$('#modified').text(t('files','Modified')); | |||
$('table').removeClass('multiselect'); | |||
$('.selectedActions').hide(); | |||
$('#select_all').removeAttr('checked'); | |||
} | |||
else { | |||
$('.selectedActions').show(); | |||
var totalSize = 0; | |||
for(var i=0; i<selectedFiles.length; i++) { | |||
totalSize+=selectedFiles[i].size; | |||
} | |||
for(var i=0; i<selectedFolders.length; i++) { | |||
totalSize+=selectedFolders[i].size; | |||
} | |||
$('#headerSize').text(humanFileSize(totalSize)); | |||
var selection = ''; | |||
if (selectedFolders.length > 0) { | |||
selection += n('files', '%n folder', '%n folders', selectedFolders.length); | |||
if (selectedFiles.length > 0) { | |||
selection += ' & '; | |||
} | |||
var files = FileList.getSelectedFiles(); | |||
if (files.length === 0) { | |||
// single one selected without checkbox? | |||
files = _.map(ui.helper.find('tr'), FileList.elementToFile); | |||
} | |||
if (selectedFiles.length>0) { | |||
selection += n('files', '%n file', '%n files', selectedFiles.length); | |||
} | |||
$('#headerName span.name').text(selection); | |||
$('#modified').text(''); | |||
$('table').addClass('multiselect'); | |||
} | |||
} | |||
/** | |||
* @brief get a list of selected files | |||
* @param {string} property (option) the property of the file requested | |||
* @return {array} | |||
* | |||
* possible values for property: name, mime, size and type | |||
* if property is set, an array with that property for each file is returnd | |||
* if it's ommited an array of objects with all properties is returned | |||
*/ | |||
Files.getSelectedFiles = function(property) { | |||
var elements=$('td.filename input:checkbox:checked').parent().parent(); | |||
var files=[]; | |||
elements.each(function(i,element) { | |||
var file={ | |||
name:$(element).attr('data-file'), | |||
mime:$(element).data('mime'), | |||
type:$(element).data('type'), | |||
size:$(element).data('size'), | |||
etag:$(element).data('etag'), | |||
origin: $(element).data('id') | |||
}; | |||
if (property) { | |||
files.push(file[property]); | |||
} else { | |||
files.push(file); | |||
} | |||
}); | |||
return files; | |||
} | |||
FileList.move(_.pluck(files, 'name'), targetPath); | |||
}, | |||
tolerance: 'pointer' | |||
}; | |||
Files.getMimeIcon = function(mime, ready) { | |||
if (Files.getMimeIcon.cache[mime]) { | |||
@@ -665,7 +437,7 @@ Files.generatePreviewUrl = function(urlSpec) { | |||
urlSpec.x *= window.devicePixelRatio; | |||
urlSpec.forceIcon = 0; | |||
return OC.generateUrl('/core/preview.png?') + $.param(urlSpec); | |||
} | |||
}; | |||
Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { | |||
// get mime icon url |
@@ -0,0 +1,195 @@ | |||
/** | |||
* ownCloud | |||
* | |||
* @author Vincent Petry | |||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public | |||
* License along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
/* global OC, n, t */ | |||
(function() { | |||
/** | |||
* The FileSummary class encapsulates the file summary values and | |||
* the logic to render it in the given container | |||
* @param $tr table row element | |||
* $param summary optional initial summary value | |||
*/ | |||
var FileSummary = function($tr) { | |||
this.$el = $tr; | |||
this.clear(); | |||
this.render(); | |||
}; | |||
FileSummary.prototype = { | |||
summary: { | |||
totalFiles: 0, | |||
totalDirs: 0, | |||
totalSize: 0 | |||
}, | |||
/** | |||
* Adds file | |||
* @param file file to add | |||
* @param update whether to update the display | |||
*/ | |||
add: function(file, update) { | |||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { | |||
this.summary.totalDirs++; | |||
} | |||
else { | |||
this.summary.totalFiles++; | |||
} | |||
this.summary.totalSize += parseInt(file.size, 10) || 0; | |||
if (!!update) { | |||
this.update(); | |||
} | |||
}, | |||
/** | |||
* Removes file | |||
* @param file file to remove | |||
* @param update whether to update the display | |||
*/ | |||
remove: function(file, update) { | |||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { | |||
this.summary.totalDirs--; | |||
} | |||
else { | |||
this.summary.totalFiles--; | |||
} | |||
this.summary.totalSize -= parseInt(file.size, 10) || 0; | |||
if (!!update) { | |||
this.update(); | |||
} | |||
}, | |||
/** | |||
* Returns the total of files and directories | |||
*/ | |||
getTotal: function() { | |||
return this.summary.totalDirs + this.summary.totalFiles; | |||
}, | |||
/** | |||
* Recalculates the summary based on the given files array | |||
* @param files array of files | |||
*/ | |||
calculate: function(files) { | |||
var file; | |||
var summary = { | |||
totalDirs: 0, | |||
totalFiles: 0, | |||
totalSize: 0 | |||
}; | |||
for (var i = 0; i < files.length; i++) { | |||
file = files[i]; | |||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') { | |||
summary.totalDirs++; | |||
} | |||
else { | |||
summary.totalFiles++; | |||
} | |||
summary.totalSize += parseInt(file.size, 10) || 0; | |||
} | |||
this.setSummary(summary); | |||
}, | |||
/** | |||
* Clears the summary | |||
*/ | |||
clear: function() { | |||
this.calculate([]); | |||
}, | |||
/** | |||
* Sets the current summary values | |||
* @param summary map | |||
*/ | |||
setSummary: function(summary) { | |||
this.summary = summary; | |||
this.update(); | |||
}, | |||
/** | |||
* Renders the file summary element | |||
*/ | |||
update: function() { | |||
if (!this.$el) { | |||
return; | |||
} | |||
if (!this.summary.totalFiles && !this.summary.totalDirs) { | |||
this.$el.addClass('hidden'); | |||
return; | |||
} | |||
// There's a summary and data -> Update the summary | |||
this.$el.removeClass('hidden'); | |||
var $dirInfo = this.$el.find('.dirinfo'); | |||
var $fileInfo = this.$el.find('.fileinfo'); | |||
var $connector = this.$el.find('.connector'); | |||
// Substitute old content with new translations | |||
$dirInfo.html(n('files', '%n folder', '%n folders', this.summary.totalDirs)); | |||
$fileInfo.html(n('files', '%n file', '%n files', this.summary.totalFiles)); | |||
this.$el.find('.filesize').html(OC.Util.humanFileSize(this.summary.totalSize)); | |||
// Show only what's necessary (may be hidden) | |||
if (this.summary.totalDirs === 0) { | |||
$dirInfo.addClass('hidden'); | |||
$connector.addClass('hidden'); | |||
} else { | |||
$dirInfo.removeClass('hidden'); | |||
} | |||
if (this.summary.totalFiles === 0) { | |||
$fileInfo.addClass('hidden'); | |||
$connector.addClass('hidden'); | |||
} else { | |||
$fileInfo.removeClass('hidden'); | |||
} | |||
if (this.summary.totalDirs > 0 && this.summary.totalFiles > 0) { | |||
$connector.removeClass('hidden'); | |||
} | |||
}, | |||
render: function() { | |||
if (!this.$el) { | |||
return; | |||
} | |||
// TODO: ideally this should be separate to a template or something | |||
var summary = this.summary; | |||
var directoryInfo = n('files', '%n folder', '%n folders', summary.totalDirs); | |||
var fileInfo = n('files', '%n file', '%n files', summary.totalFiles); | |||
var infoVars = { | |||
dirs: '<span class="dirinfo">'+directoryInfo+'</span><span class="connector">', | |||
files: '</span><span class="fileinfo">'+fileInfo+'</span>' | |||
}; | |||
// don't show the filesize column, if filesize is NaN (e.g. in trashbin) | |||
var fileSize = ''; | |||
if (!isNaN(summary.totalSize)) { | |||
fileSize = '<td class="filesize">' + OC.Util.humanFileSize(summary.totalSize) + '</td>'; | |||
} | |||
var info = t('files', '{dirs} and {files}', infoVars); | |||
var $summary = $('<td><span class="info">'+info+'</span></td>'+fileSize+'<td></td>'); | |||
if (!this.summary.totalFiles && !this.summary.totalDirs) { | |||
this.$el.addClass('hidden'); | |||
} | |||
this.$el.append($summary); | |||
} | |||
}; | |||
window.FileSummary = FileSummary; | |||
})(); | |||
@@ -91,6 +91,8 @@ | |||
</thead> | |||
<tbody id="fileList"> | |||
</tbody> | |||
<tfoot> | |||
</tfoot> | |||
</table> | |||
<div id="editor"></div><!-- FIXME Do not use this div in your app! It is deprecated and will be removed in the future! --> | |||
<div id="uploadsize-message" title="<?php p($l->t('Upload too large'))?>"> |
@@ -30,6 +30,7 @@ describe('FileActions tests', function() { | |||
$body.append('<input type="hidden" id="permissions" value="31"></input>'); | |||
// dummy files table | |||
$filesTable = $body.append('<table id="filestable"></table>'); | |||
FileList.files = []; | |||
}); | |||
afterEach(function() { | |||
$('#dir, #permissions, #filestable').remove(); |
@@ -24,6 +24,33 @@ describe('FileList tests', function() { | |||
var testFiles, alertStub, notificationStub, | |||
pushStateStub; | |||
/** | |||
* Generate test file data | |||
*/ | |||
function generateFiles(startIndex, endIndex) { | |||
var files = []; | |||
var name; | |||
for (var i = startIndex; i <= endIndex; i++) { | |||
name = 'File with index '; | |||
if (i < 10) { | |||
// do not rely on localeCompare here | |||
// and make the sorting predictable | |||
// cross-browser | |||
name += '0'; | |||
} | |||
name += i + '.txt'; | |||
files.push({ | |||
id: i, | |||
type: 'file', | |||
name: name, | |||
mimetype: 'text/plain', | |||
size: i * 2, | |||
etag: 'abc' | |||
}); | |||
} | |||
return files; | |||
} | |||
beforeEach(function() { | |||
// init horrible parameters | |||
var $body = $('body'); | |||
@@ -48,9 +75,17 @@ describe('FileList tests', function() { | |||
' <div class="notCreatable"></div>' + | |||
'</div>' + | |||
// dummy table | |||
// TODO: at some point this will be rendered by the FileList class itself! | |||
'<table id="filestable">' + | |||
'<thead><tr><th class="hidden">Name</th></tr></thead>' + | |||
'<thead><tr><th id="headerName" class="hidden">' + | |||
'<input type="checkbox" id="select_all">' + | |||
'<span class="name">Name</span>' + | |||
'<span class="selectedActions hidden">' + | |||
'<a href class="download">Download</a>' + | |||
'<a href class="delete-selected">Delete</a></span>' + | |||
'</th></tr></thead>' + | |||
'<tbody id="fileList"></tbody>' + | |||
'<tfoot></tfoot>' + | |||
'</table>' + | |||
'<div id="emptycontent">Empty content message</div>' | |||
); | |||
@@ -60,25 +95,29 @@ describe('FileList tests', function() { | |||
type: 'file', | |||
name: 'One.txt', | |||
mimetype: 'text/plain', | |||
size: 12 | |||
size: 12, | |||
etag: 'abc' | |||
}, { | |||
id: 2, | |||
type: 'file', | |||
name: 'Two.jpg', | |||
mimetype: 'image/jpeg', | |||
size: 12049 | |||
size: 12049, | |||
etag: 'def', | |||
}, { | |||
id: 3, | |||
type: 'file', | |||
name: 'Three.pdf', | |||
mimetype: 'application/pdf', | |||
size: 58009 | |||
size: 58009, | |||
etag: '123', | |||
}, { | |||
id: 4, | |||
type: 'dir', | |||
name: 'somedir', | |||
mimetype: 'httpd/unix-directory', | |||
size: 250 | |||
size: 250, | |||
etag: '456' | |||
}]; | |||
FileList.initialize(); | |||
@@ -220,25 +259,65 @@ describe('FileList tests', function() { | |||
var $tr = FileList.add(fileData); | |||
expect($tr.find('.filesize').text()).toEqual('0 B'); | |||
}); | |||
it('adds new file to the end of the list before the summary', function() { | |||
it('adds new file to the end of the list', function() { | |||
var $tr; | |||
var fileData = { | |||
type: 'file', | |||
name: 'P comes after O.txt' | |||
name: 'ZZZ.txt' | |||
}; | |||
FileList.setFiles(testFiles); | |||
$tr = FileList.add(fileData); | |||
expect($tr.index()).toEqual(4); | |||
expect($tr.next().hasClass('summary')).toEqual(true); | |||
}); | |||
it('adds new file at correct position in insert mode', function() { | |||
it('inserts files in a sorted manner when insert option is enabled', function() { | |||
var $tr; | |||
for (var i = 0; i < testFiles.length; i++) { | |||
FileList.add(testFiles[i]); | |||
} | |||
expect(FileList.files[0].name).toEqual('somedir'); | |||
expect(FileList.files[1].name).toEqual('One.txt'); | |||
expect(FileList.files[2].name).toEqual('Three.pdf'); | |||
expect(FileList.files[3].name).toEqual('Two.jpg'); | |||
}); | |||
it('inserts new file at correct position', function() { | |||
var $tr; | |||
var fileData = { | |||
type: 'file', | |||
name: 'P comes after O.txt' | |||
}; | |||
FileList.setFiles(testFiles); | |||
$tr = FileList.add(fileData, {insert: true}); | |||
for (var i = 0; i < testFiles.length; i++) { | |||
FileList.add(testFiles[i]); | |||
} | |||
$tr = FileList.add(fileData); | |||
// after "One.txt" | |||
expect($tr.index()).toEqual(2); | |||
expect(FileList.files[2]).toEqual(fileData); | |||
}); | |||
it('inserts new folder at correct position in insert mode', function() { | |||
var $tr; | |||
var fileData = { | |||
type: 'dir', | |||
name: 'somedir2 comes after somedir' | |||
}; | |||
for (var i = 0; i < testFiles.length; i++) { | |||
FileList.add(testFiles[i]); | |||
} | |||
$tr = FileList.add(fileData); | |||
expect($tr.index()).toEqual(1); | |||
expect(FileList.files[1]).toEqual(fileData); | |||
}); | |||
it('inserts new file at the end correctly', function() { | |||
var $tr; | |||
var fileData = { | |||
type: 'file', | |||
name: 'zzz.txt' | |||
}; | |||
for (var i = 0; i < testFiles.length; i++) { | |||
FileList.add(testFiles[i]); | |||
} | |||
$tr = FileList.add(fileData); | |||
expect($tr.index()).toEqual(4); | |||
expect(FileList.files[4]).toEqual(fileData); | |||
}); | |||
it('removes empty content message and shows summary when adding first file', function() { | |||
var fileData = { | |||
@@ -249,8 +328,8 @@ describe('FileList tests', function() { | |||
FileList.setFiles([]); | |||
expect(FileList.isEmpty).toEqual(true); | |||
FileList.add(fileData); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(1); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
// yes, ugly... | |||
expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); | |||
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); | |||
@@ -268,11 +347,12 @@ describe('FileList tests', function() { | |||
$removedEl = FileList.remove('One.txt'); | |||
expect($removedEl).toBeDefined(); | |||
expect($removedEl.attr('data-file')).toEqual('One.txt'); | |||
expect($('#fileList tr:not(.summary)').length).toEqual(3); | |||
expect($('#fileList tr').length).toEqual(3); | |||
expect(FileList.files.length).toEqual(3); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(1); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.info').text()).toEqual('1 folder and 2 files'); | |||
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false); | |||
@@ -282,11 +362,12 @@ describe('FileList tests', function() { | |||
it('Shows empty content when removing last file', function() { | |||
FileList.setFiles([testFiles[0]]); | |||
FileList.remove('One.txt'); | |||
expect($('#fileList tr:not(.summary)').length).toEqual(0); | |||
expect($('#fileList tr').length).toEqual(0); | |||
expect(FileList.files.length).toEqual(0); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(0); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(true); | |||
expect($('#filestable thead th').hasClass('hidden')).toEqual(true); | |||
expect($('#emptycontent').hasClass('hidden')).toEqual(false); | |||
expect(FileList.isEmpty).toEqual(true); | |||
@@ -318,10 +399,10 @@ describe('FileList tests', function() { | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
expect(FileList.findFileEl('Two.jpg').length).toEqual(0); | |||
expect(FileList.findFileEl('Three.pdf').length).toEqual(1); | |||
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(2); | |||
expect(FileList.$fileList.find('tr').length).toEqual(2); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(1); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.info').text()).toEqual('1 folder and 1 file'); | |||
expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false); | |||
@@ -342,11 +423,12 @@ describe('FileList tests', function() { | |||
JSON.stringify({status: 'success'}) | |||
); | |||
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(0); | |||
expect(FileList.$fileList.find('tr').length).toEqual(0); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(0); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(true); | |||
expect(FileList.isEmpty).toEqual(true); | |||
expect(FileList.files.length).toEqual(0); | |||
expect($('#filestable thead th').hasClass('hidden')).toEqual(true); | |||
expect($('#emptycontent').hasClass('hidden')).toEqual(false); | |||
}); | |||
@@ -363,7 +445,7 @@ describe('FileList tests', function() { | |||
// files are still in the list | |||
expect(FileList.findFileEl('One.txt').length).toEqual(1); | |||
expect(FileList.findFileEl('Two.jpg').length).toEqual(1); | |||
expect(FileList.$fileList.find('tr:not(.summary)').length).toEqual(4); | |||
expect(FileList.$fileList.find('tr').length).toEqual(4); | |||
expect(notificationStub.calledOnce).toEqual(true); | |||
}); | |||
@@ -372,37 +454,41 @@ describe('FileList tests', function() { | |||
function doRename() { | |||
var $input, request; | |||
FileList.setFiles(testFiles); | |||
for (var i = 0; i < testFiles.length; i++) { | |||
FileList.add(testFiles[i]); | |||
} | |||
// trigger rename prompt | |||
FileList.rename('One.txt'); | |||
$input = FileList.$fileList.find('input.filename'); | |||
$input.val('One_renamed.txt').blur(); | |||
$input.val('Tu_after_three.txt').blur(); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
var request = fakeServer.requests[0]; | |||
request = fakeServer.requests[0]; | |||
expect(request.url.substr(0, request.url.indexOf('?'))).toEqual(OC.webroot + '/index.php/apps/files/ajax/rename.php'); | |||
expect(OC.parseQueryString(request.url)).toEqual({'dir': '/subdir', newname: 'One_renamed.txt', file: 'One.txt'}); | |||
expect(OC.parseQueryString(request.url)).toEqual({'dir': '/subdir', newname: 'Tu_after_three.txt', file: 'One.txt'}); | |||
// element is renamed before the request finishes | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(1); | |||
expect(FileList.findFileEl('Tu_after_three.txt').length).toEqual(1); | |||
// input is gone | |||
expect(FileList.$fileList.find('input.filename').length).toEqual(0); | |||
} | |||
it('Keeps renamed file entry if rename ajax call suceeded', function() { | |||
it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() { | |||
doRename(); | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'One_renamed.txt' | |||
name: 'Tu_after_three.txt', | |||
type: 'file' | |||
} | |||
})); | |||
// element stays renamed | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(1); | |||
expect(FileList.findFileEl('Tu_after_three.txt').length).toEqual(1); | |||
expect(FileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.txt | |||
expect(alertStub.notCalled).toEqual(true); | |||
}); | |||
@@ -418,7 +504,8 @@ describe('FileList tests', function() { | |||
// element was reverted | |||
expect(FileList.findFileEl('One.txt').length).toEqual(1); | |||
expect(FileList.findFileEl('One_renamed.txt').length).toEqual(0); | |||
expect(FileList.findFileEl('One.txt').index()).toEqual(1); // after somedir | |||
expect(FileList.findFileEl('Tu_after_three.txt').length).toEqual(0); | |||
expect(alertStub.calledOnce).toEqual(true); | |||
}); | |||
@@ -429,12 +516,12 @@ describe('FileList tests', function() { | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'One_renamed.txt' | |||
name: 'Tu_after_three.txt' | |||
} | |||
})); | |||
$tr = FileList.findFileEl('One_renamed.txt'); | |||
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=One_renamed.txt'); | |||
$tr = FileList.findFileEl('Tu_after_three.txt'); | |||
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=Tu_after_three.txt'); | |||
}); | |||
// FIXME: fix this in the source code! | |||
xit('Correctly updates file link after rename when path has same name', function() { | |||
@@ -446,27 +533,122 @@ describe('FileList tests', function() { | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'One_renamed.txt' | |||
name: 'Tu_after_three.txt' | |||
} | |||
})); | |||
$tr = FileList.findFileEl('One_renamed.txt'); | |||
$tr = FileList.findFileEl('Tu_after_three.txt'); | |||
expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=One.txt'); | |||
}); | |||
}); | |||
describe('Moving files', function() { | |||
beforeEach(function() { | |||
FileList.setFiles(testFiles); | |||
}); | |||
it('Moves single file to target folder', function() { | |||
var request; | |||
FileList.move('One.txt', '/somedir'); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php'); | |||
expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'}); | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'One.txt', | |||
type: 'file' | |||
} | |||
})); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
// folder size has increased | |||
expect(FileList.findFileEl('somedir').data('size')).toEqual(262); | |||
expect(FileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B'); | |||
expect(notificationStub.notCalled).toEqual(true); | |||
}); | |||
it('Moves list of files to target folder', function() { | |||
var request; | |||
FileList.move(['One.txt', 'Two.jpg'], '/somedir'); | |||
expect(fakeServer.requests.length).toEqual(2); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php'); | |||
expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'}); | |||
request = fakeServer.requests[1]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php'); | |||
expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'Two.jpg', target: '/somedir'}); | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'One.txt', | |||
type: 'file' | |||
} | |||
})); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
// folder size has increased | |||
expect(FileList.findFileEl('somedir').data('size')).toEqual(262); | |||
expect(FileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B'); | |||
fakeServer.requests[1].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'success', | |||
data: { | |||
name: 'Two.jpg', | |||
type: 'file' | |||
} | |||
})); | |||
expect(FileList.findFileEl('Two.jpg').length).toEqual(0); | |||
// folder size has increased | |||
expect(FileList.findFileEl('somedir').data('size')).toEqual(12311); | |||
expect(FileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 kB'); | |||
expect(notificationStub.notCalled).toEqual(true); | |||
}); | |||
it('Shows notification if a file could not be moved', function() { | |||
var request; | |||
FileList.move('One.txt', '/somedir'); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php'); | |||
expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'}); | |||
fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ | |||
status: 'error', | |||
data: { | |||
message: 'Error while moving file', | |||
} | |||
})); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(1); | |||
expect(notificationStub.calledOnce).toEqual(true); | |||
expect(notificationStub.getCall(0).args[0]).toEqual('Error while moving file'); | |||
}); | |||
}); | |||
describe('List rendering', function() { | |||
it('renders a list of files using add()', function() { | |||
var addSpy = sinon.spy(FileList, 'add'); | |||
expect(FileList.files.length).toEqual(0); | |||
expect(FileList.files).toEqual([]); | |||
FileList.setFiles(testFiles); | |||
expect(addSpy.callCount).toEqual(4); | |||
expect($('#fileList tr:not(.summary)').length).toEqual(4); | |||
addSpy.restore(); | |||
expect($('#fileList tr').length).toEqual(4); | |||
expect(FileList.files.length).toEqual(4); | |||
expect(FileList.files).toEqual(testFiles); | |||
}); | |||
it('updates summary using the file sizes', function() { | |||
var $summary; | |||
FileList.setFiles(testFiles); | |||
$summary = $('#fileList .summary'); | |||
expect($summary.length).toEqual(1); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); | |||
expect($summary.find('.filesize').text()).toEqual('69 kB'); | |||
}); | |||
@@ -474,20 +656,20 @@ describe('FileList tests', function() { | |||
FileList.setFiles(testFiles); | |||
expect($('#filestable thead th').hasClass('hidden')).toEqual(false); | |||
expect($('#emptycontent').hasClass('hidden')).toEqual(true); | |||
expect(FileList.$fileList.find('.summary').length).toEqual(1); | |||
expect(FileList.$el.find('.summary').hasClass('hidden')).toEqual(false); | |||
}); | |||
it('hides headers, summary and show empty content message after setting empty file list', function(){ | |||
FileList.setFiles([]); | |||
expect($('#filestable thead th').hasClass('hidden')).toEqual(true); | |||
expect($('#emptycontent').hasClass('hidden')).toEqual(false); | |||
expect(FileList.$fileList.find('.summary').length).toEqual(0); | |||
expect(FileList.$el.find('.summary').hasClass('hidden')).toEqual(true); | |||
}); | |||
it('hides headers, empty content message, and summary when list is empty and user has no creation permission', function(){ | |||
$('#permissions').val(0); | |||
FileList.setFiles([]); | |||
expect($('#filestable thead th').hasClass('hidden')).toEqual(true); | |||
expect($('#emptycontent').hasClass('hidden')).toEqual(true); | |||
expect(FileList.$fileList.find('.summary').length).toEqual(0); | |||
expect(FileList.$el.find('.summary').hasClass('hidden')).toEqual(true); | |||
}); | |||
it('calling findFileEl() can find existing file element', function() { | |||
FileList.setFiles(testFiles); | |||
@@ -519,6 +701,110 @@ describe('FileList tests', function() { | |||
FileList.setFiles(testFiles); | |||
expect(handler.calledOnce).toEqual(true); | |||
}); | |||
it('does not update summary when removing non-existing files', function() { | |||
// single file | |||
FileList.setFiles([testFiles[0]]); | |||
$summary = $('#filestable .summary'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); | |||
FileList.remove('unexist.txt'); | |||
expect($summary.hasClass('hidden')).toEqual(false); | |||
expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); | |||
}); | |||
}); | |||
describe('Rendering next page on scroll', function() { | |||
beforeEach(function() { | |||
FileList.setFiles(generateFiles(0, 64)); | |||
}); | |||
it('renders only the first page', function() { | |||
expect(FileList.files.length).toEqual(65); | |||
expect($('#fileList tr').length).toEqual(20); | |||
}); | |||
it('renders the second page when scrolling down (trigger nextPage)', function() { | |||
// TODO: can't simulate scrolling here, so calling nextPage directly | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(40); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(60); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(65); | |||
FileList._nextPage(true); | |||
// stays at 65 | |||
expect($('#fileList tr').length).toEqual(65); | |||
}); | |||
it('inserts into the DOM if insertion point is in the visible page ', function() { | |||
FileList.add({ | |||
id: 2000, | |||
type: 'file', | |||
name: 'File with index 15b.txt' | |||
}); | |||
expect($('#fileList tr').length).toEqual(21); | |||
expect(FileList.findFileEl('File with index 15b.txt').index()).toEqual(16); | |||
}); | |||
it('does not inserts into the DOM if insertion point is not the visible page ', function() { | |||
FileList.add({ | |||
id: 2000, | |||
type: 'file', | |||
name: 'File with index 28b.txt' | |||
}); | |||
expect($('#fileList tr').length).toEqual(20); | |||
expect(FileList.findFileEl('File with index 28b.txt').length).toEqual(0); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(40); | |||
expect(FileList.findFileEl('File with index 28b.txt').index()).toEqual(29); | |||
}); | |||
it('appends into the DOM when inserting a file after the last visible element', function() { | |||
FileList.add({ | |||
id: 2000, | |||
type: 'file', | |||
name: 'File with index 19b.txt' | |||
}); | |||
expect($('#fileList tr').length).toEqual(21); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(41); | |||
}); | |||
it('appends into the DOM when inserting a file on the last page when visible', function() { | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(40); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(60); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(65); | |||
FileList._nextPage(true); | |||
FileList.add({ | |||
id: 2000, | |||
type: 'file', | |||
name: 'File with index 88.txt' | |||
}); | |||
expect($('#fileList tr').length).toEqual(66); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(66); | |||
}); | |||
it('shows additional page when appending a page of files and scrolling down', function() { | |||
var newFiles = generateFiles(66, 81); | |||
for (var i = 0; i < newFiles.length; i++) { | |||
FileList.add(newFiles[i]); | |||
} | |||
expect($('#fileList tr').length).toEqual(20); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(40); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(60); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(80); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(81); | |||
FileList._nextPage(true); | |||
expect($('#fileList tr').length).toEqual(81); | |||
}); | |||
it('automatically renders next page when there are not enough elements visible', function() { | |||
// delete the 15 first elements | |||
for (var i = 0; i < 15; i++) { | |||
FileList.remove(FileList.files[0].name); | |||
} | |||
// still makes sure that there are 20 elements visible, if any | |||
expect($('#fileList tr').length).toEqual(25); | |||
}); | |||
}); | |||
describe('file previews', function() { | |||
var previewLoadStub; | |||
@@ -642,7 +928,7 @@ describe('FileList tests', function() { | |||
var query = url.substr(url.indexOf('?') + 1); | |||
expect(OC.parseQueryString(query)).toEqual({'dir': '/subdir'}); | |||
fakeServer.respond(); | |||
expect($('#fileList tr:not(.summary)').length).toEqual(4); | |||
expect($('#fileList tr').length).toEqual(4); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(1); | |||
}); | |||
it('switches dir and fetches file list when calling changeDirectory()', function() { | |||
@@ -740,14 +1026,12 @@ describe('FileList tests', function() { | |||
} | |||
}; | |||
// returns a list of tr that were dragged | |||
// FIXME: why are their attributes different than the | |||
// regular file trs ? | |||
ui.helper.find.returns([ | |||
$('<tr data-filename="One.txt" data-dir="' + testDir + '"></tr>'), | |||
$('<tr data-filename="Two.jpg" data-dir="' + testDir + '"></tr>') | |||
$('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'), | |||
$('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>') | |||
]); | |||
// simulate drop event | |||
FileList._onDropOnBreadCrumb.call($crumb, new $.Event('drop'), ui); | |||
FileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui); | |||
// will trigger two calls to move.php (first one was previous list.php) | |||
expect(fakeServer.requests.length).toEqual(3); | |||
@@ -784,14 +1068,12 @@ describe('FileList tests', function() { | |||
} | |||
}; | |||
// returns a list of tr that were dragged | |||
// FIXME: why are their attributes different than the | |||
// regular file trs ? | |||
ui.helper.find.returns([ | |||
$('<tr data-filename="One.txt" data-dir="' + testDir + '"></tr>'), | |||
$('<tr data-filename="Two.jpg" data-dir="' + testDir + '"></tr>') | |||
$('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'), | |||
$('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>') | |||
]); | |||
// simulate drop event | |||
FileList._onDropOnBreadCrumb.call($crumb, new $.Event('drop'), ui); | |||
FileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui); | |||
// no extra server request | |||
expect(fakeServer.requests.length).toEqual(1); | |||
@@ -811,4 +1093,329 @@ describe('FileList tests', function() { | |||
expect(Files.getAjaxUrl('test', {a:1, b:'x y'})).toEqual(OC.webroot + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y'); | |||
}); | |||
}); | |||
describe('File selection', function() { | |||
beforeEach(function() { | |||
FileList.setFiles(testFiles); | |||
}); | |||
it('Selects a file when clicking its checkbox', function() { | |||
var $tr = FileList.findFileEl('One.txt'); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(false); | |||
$tr.find('td.filename input:checkbox').click(); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
}); | |||
it('Selects/deselect a file when clicking on the name while holding Ctrl', function() { | |||
var $tr = FileList.findFileEl('One.txt'); | |||
var $tr2 = FileList.findFileEl('Three.pdf'); | |||
var e; | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(false); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(false); | |||
e = new $.Event('click'); | |||
e.ctrlKey = true; | |||
$tr.find('td.filename .name').trigger(e); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(false); | |||
// click on second entry, does not clear the selection | |||
e = new $.Event('click'); | |||
e.ctrlKey = true; | |||
$tr2.find('td.filename .name').trigger(e); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name')).toEqual(['One.txt', 'Three.pdf']); | |||
// deselect now | |||
e = new $.Event('click'); | |||
e.ctrlKey = true; | |||
$tr2.find('td.filename .name').trigger(e); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(false); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name')).toEqual(['One.txt']); | |||
}); | |||
it('Selects a range when clicking on one file then Shift clicking on another one', function() { | |||
var $tr = FileList.findFileEl('One.txt'); | |||
var $tr2 = FileList.findFileEl('Three.pdf'); | |||
var e; | |||
$tr.find('td.filename input:checkbox').click(); | |||
e = new $.Event('click'); | |||
e.shiftKey = true; | |||
$tr2.find('td.filename .name').trigger(e); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect(FileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true); | |||
var selection = _.pluck(FileList.getSelectedFiles(), 'name'); | |||
expect(selection.length).toEqual(3); | |||
expect(selection).toContain('One.txt'); | |||
expect(selection).toContain('Two.jpg'); | |||
expect(selection).toContain('Three.pdf'); | |||
}); | |||
it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() { | |||
var $tr = FileList.findFileEl('One.txt'); | |||
var $tr2 = FileList.findFileEl('Three.pdf'); | |||
var e; | |||
$tr2.find('td.filename input:checkbox').click(); | |||
e = new $.Event('click'); | |||
e.shiftKey = true; | |||
$tr.find('td.filename .name').trigger(e); | |||
expect($tr.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect($tr2.find('input:checkbox').prop('checked')).toEqual(true); | |||
expect(FileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true); | |||
var selection = _.pluck(FileList.getSelectedFiles(), 'name'); | |||
expect(selection.length).toEqual(3); | |||
expect(selection).toContain('One.txt'); | |||
expect(selection).toContain('Two.jpg'); | |||
expect(selection).toContain('Three.pdf'); | |||
}); | |||
it('Selecting all files will automatically check "select all" checkbox', function() { | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
$('#fileList tr td.filename input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
}); | |||
it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() { | |||
FileList.setFiles(generateFiles(0, 41)); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
$('#fileList tr td.filename input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
}); | |||
it('Clicking "select all" will select/deselect all files', function() { | |||
FileList.setFiles(generateFiles(0, 41)); | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
$('#fileList tr input:checkbox').each(function() { | |||
expect($(this).prop('checked')).toEqual(true); | |||
}); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(42); | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
$('#fileList tr input:checkbox').each(function() { | |||
expect($(this).prop('checked')).toEqual(false); | |||
}); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(0); | |||
}); | |||
it('Clicking "select all" then deselecting a file will uncheck "select all"', function() { | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
var $tr = FileList.findFileEl('One.txt'); | |||
$tr.find('input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(3); | |||
}); | |||
it('Updates the selection summary when doing a few manipulations with "Select all"', function() { | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
var $tr = FileList.findFileEl('One.txt'); | |||
// unselect one | |||
$tr.find('input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(3); | |||
// select all | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(4); | |||
// unselect one | |||
$tr.find('input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(3); | |||
// re-select it | |||
$tr.find('input:checkbox').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(4); | |||
}); | |||
it('Auto-selects files on next page when "select all" is checked', function() { | |||
FileList.setFiles(generateFiles(0, 41)); | |||
$('#select_all').click(); | |||
expect(FileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20); | |||
FileList._nextPage(true); | |||
expect(FileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40); | |||
FileList._nextPage(true); | |||
expect(FileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name').length).toEqual(42); | |||
}); | |||
it('Selecting files updates selection summary', function() { | |||
var $summary = $('#headerName span.name'); | |||
expect($summary.text()).toEqual('Name'); | |||
FileList.findFileEl('One.txt').find('input:checkbox').click(); | |||
FileList.findFileEl('Three.pdf').find('input:checkbox').click(); | |||
FileList.findFileEl('somedir').find('input:checkbox').click(); | |||
expect($summary.text()).toEqual('1 folder & 2 files'); | |||
}); | |||
it('Unselecting files hides selection summary', function() { | |||
var $summary = $('#headerName span.name'); | |||
FileList.findFileEl('One.txt').find('input:checkbox').click().click(); | |||
expect($summary.text()).toEqual('Name'); | |||
}); | |||
it('Select/deselect files shows/hides file actions', function() { | |||
var $actions = $('#headerName .selectedActions'); | |||
var $checkbox = FileList.findFileEl('One.txt').find('input:checkbox'); | |||
expect($actions.hasClass('hidden')).toEqual(true); | |||
$checkbox.click(); | |||
expect($actions.hasClass('hidden')).toEqual(false); | |||
$checkbox.click(); | |||
expect($actions.hasClass('hidden')).toEqual(true); | |||
}); | |||
it('Selection is cleared when switching dirs', function() { | |||
$('#select_all').click(); | |||
var data = { | |||
status: 'success', | |||
data: { | |||
files: testFiles, | |||
permissions: 31 | |||
} | |||
}; | |||
fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php/, [ | |||
200, { | |||
"Content-Type": "application/json" | |||
}, | |||
JSON.stringify(data) | |||
]); | |||
FileList.changeDirectory('/'); | |||
fakeServer.respond(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
expect(_.pluck(FileList.getSelectedFiles(), 'name')).toEqual([]); | |||
}); | |||
it('getSelectedFiles returns the selected files even when they are on the next page', function() { | |||
var selectedFiles; | |||
FileList.setFiles(generateFiles(0, 41)); | |||
$('#select_all').click(); | |||
// unselect one to not have the "allFiles" case | |||
FileList.$fileList.find('tr input:checkbox:first').click(); | |||
// only 20 files visible, must still return all the selected ones | |||
selectedFiles = _.pluck(FileList.getSelectedFiles(), 'name'); | |||
expect(selectedFiles.length).toEqual(41); | |||
}); | |||
describe('Actions', function() { | |||
beforeEach(function() { | |||
FileList.findFileEl('One.txt').find('input:checkbox').click(); | |||
FileList.findFileEl('Three.pdf').find('input:checkbox').click(); | |||
FileList.findFileEl('somedir').find('input:checkbox').click(); | |||
}); | |||
it('getSelectedFiles returns the selected file data', function() { | |||
var files = FileList.getSelectedFiles(); | |||
expect(files.length).toEqual(3); | |||
expect(files[0]).toEqual({ | |||
id: 1, | |||
name: 'One.txt', | |||
mimetype: 'text/plain', | |||
type: 'file', | |||
size: 12, | |||
etag: 'abc' | |||
}); | |||
expect(files[1]).toEqual({ | |||
id: 3, | |||
type: 'file', | |||
name: 'Three.pdf', | |||
mimetype: 'application/pdf', | |||
size: 58009, | |||
etag: '123' | |||
}); | |||
expect(files[2]).toEqual({ | |||
id: 4, | |||
type: 'dir', | |||
name: 'somedir', | |||
mimetype: 'httpd/unix-directory', | |||
size: 250, | |||
etag: '456' | |||
}); | |||
}); | |||
it('Removing a file removes it from the selection', function() { | |||
FileList.remove('Three.pdf'); | |||
var files = FileList.getSelectedFiles(); | |||
expect(files.length).toEqual(2); | |||
expect(files[0]).toEqual({ | |||
id: 1, | |||
name: 'One.txt', | |||
mimetype: 'text/plain', | |||
type: 'file', | |||
size: 12, | |||
etag: 'abc' | |||
}); | |||
expect(files[1]).toEqual({ | |||
id: 4, | |||
type: 'dir', | |||
name: 'somedir', | |||
mimetype: 'httpd/unix-directory', | |||
size: 250, | |||
etag: '456' | |||
}); | |||
}); | |||
describe('Download', function() { | |||
it('Opens download URL when clicking "Download"', function() { | |||
var redirectStub = sinon.stub(OC, 'redirect'); | |||
$('.selectedActions .download').click(); | |||
expect(redirectStub.calledOnce).toEqual(true); | |||
expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D'); | |||
redirectStub.restore(); | |||
}); | |||
it('Downloads root folder when all selected in root folder', function() { | |||
$('#dir').val('/'); | |||
$('#select_all').click(); | |||
var redirectStub = sinon.stub(OC, 'redirect'); | |||
$('.selectedActions .download').click(); | |||
expect(redirectStub.calledOnce).toEqual(true); | |||
expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files='); | |||
redirectStub.restore(); | |||
}); | |||
it('Downloads parent folder when all selected in subfolder', function() { | |||
$('#select_all').click(); | |||
var redirectStub = sinon.stub(OC, 'redirect'); | |||
$('.selectedActions .download').click(); | |||
expect(redirectStub.calledOnce).toEqual(true); | |||
expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir'); | |||
redirectStub.restore(); | |||
}); | |||
}); | |||
describe('Delete', function() { | |||
it('Deletes selected files when "Delete" clicked', function() { | |||
var request; | |||
$('.selectedActions .delete-selected').click(); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/subdir', files: '["One.txt","Three.pdf","somedir"]'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({status: 'success'}) | |||
); | |||
expect(FileList.findFileEl('One.txt').length).toEqual(0); | |||
expect(FileList.findFileEl('Three.pdf').length).toEqual(0); | |||
expect(FileList.findFileEl('somedir').length).toEqual(0); | |||
expect(FileList.findFileEl('Two.jpg').length).toEqual(1); | |||
}); | |||
it('Deletes all files when all selected when "Delete" clicked', function() { | |||
var request; | |||
$('#select_all').click(); | |||
$('.selectedActions .delete-selected').click(); | |||
expect(fakeServer.requests.length).toEqual(1); | |||
request = fakeServer.requests[0]; | |||
expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php'); | |||
expect(OC.parseQueryString(request.requestBody)) | |||
.toEqual({'dir': '/subdir', allfiles: 'true'}); | |||
fakeServer.requests[0].respond( | |||
200, | |||
{ 'Content-Type': 'application/json' }, | |||
JSON.stringify({status: 'success'}) | |||
); | |||
expect(FileList.isEmpty).toEqual(true); | |||
}); | |||
}); | |||
}); | |||
}); | |||
}); |
@@ -19,7 +19,7 @@ | |||
* | |||
*/ | |||
/* global Files */ | |||
/* global OC, Files */ | |||
describe('Files tests', function() { | |||
describe('File name validation', function() { | |||
it('Validates correct file names', function() { | |||
@@ -82,4 +82,30 @@ describe('Files tests', function() { | |||
} | |||
}); | |||
}); | |||
describe('getDownloadUrl', function() { | |||
var curDirStub; | |||
beforeEach(function() { | |||
curDirStub = sinon.stub(FileList, 'getCurrentDirectory'); | |||
}); | |||
afterEach(function() { | |||
curDirStub.restore(); | |||
}); | |||
it('returns the ajax download URL when only filename specified', function() { | |||
curDirStub.returns('/subdir'); | |||
var url = Files.getDownloadUrl('test file.txt'); | |||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt'); | |||
}); | |||
it('returns the ajax download URL when filename and dir specified', function() { | |||
var url = Files.getDownloadUrl('test file.txt', '/subdir'); | |||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=test%20file.txt'); | |||
}); | |||
it('returns the ajax download URL when filename and root dir specific', function() { | |||
var url = Files.getDownloadUrl('test file.txt', '/'); | |||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=test%20file.txt'); | |||
}); | |||
it('returns the ajax download URL when multiple files specified', function() { | |||
var url = Files.getDownloadUrl(['test file.txt', 'abc.txt'], '/subdir'); | |||
expect(url).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22test%20file.txt%22%2C%22abc.txt%22%5D'); | |||
}); | |||
}); | |||
}); |
@@ -0,0 +1,87 @@ | |||
/** | |||
* ownCloud | |||
* | |||
* @author Vincent Petry | |||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com> | |||
* | |||
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or any later version. | |||
* | |||
* This library is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public | |||
* License along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
/* global FileSummary */ | |||
describe('FileSummary tests', function() { | |||
var $container; | |||
beforeEach(function() { | |||
$container = $('<table><tr></tr></table>').find('tr'); | |||
}); | |||
afterEach(function() { | |||
$container = null; | |||
}); | |||
it('renders summary as text', function() { | |||
var s = new FileSummary($container); | |||
s.setSummary({ | |||
totalDirs: 5, | |||
totalFiles: 2, | |||
totalSize: 256000 | |||
}); | |||
expect($container.hasClass('hidden')).toEqual(false); | |||
expect($container.find('.info').text()).toEqual('5 folders and 2 files'); | |||
expect($container.find('.filesize').text()).toEqual('250 kB'); | |||
}); | |||
it('hides summary when no files or folders', function() { | |||
var s = new FileSummary($container); | |||
s.setSummary({ | |||
totalDirs: 0, | |||
totalFiles: 0, | |||
totalSize: 0 | |||
}); | |||
expect($container.hasClass('hidden')).toEqual(true); | |||
}); | |||
it('increases summary when adding files', function() { | |||
var s = new FileSummary($container); | |||
s.setSummary({ | |||
totalDirs: 5, | |||
totalFiles: 2, | |||
totalSize: 256000 | |||
}); | |||
s.add({type: 'file', size: 256000}); | |||
s.add({type: 'dir', size: 100}); | |||
s.update(); | |||
expect($container.hasClass('hidden')).toEqual(false); | |||
expect($container.find('.info').text()).toEqual('6 folders and 3 files'); | |||
expect($container.find('.filesize').text()).toEqual('500 kB'); | |||
expect(s.summary.totalDirs).toEqual(6); | |||
expect(s.summary.totalFiles).toEqual(3); | |||
expect(s.summary.totalSize).toEqual(512100); | |||
}); | |||
it('decreases summary when removing files', function() { | |||
var s = new FileSummary($container); | |||
s.setSummary({ | |||
totalDirs: 5, | |||
totalFiles: 2, | |||
totalSize: 256000 | |||
}); | |||
s.remove({type: 'file', size: 128000}); | |||
s.remove({type: 'dir', size: 100}); | |||
s.update(); | |||
expect($container.hasClass('hidden')).toEqual(false); | |||
expect($container.find('.info').text()).toEqual('4 folders and 1 file'); | |||
expect($container.find('.filesize').text()).toEqual('125 kB'); | |||
expect(s.summary.totalDirs).toEqual(4); | |||
expect(s.summary.totalFiles).toEqual(1); | |||
expect(s.summary.totalSize).toEqual(127900); | |||
}); | |||
}); |
@@ -138,6 +138,7 @@ if (isset($path)) { | |||
OCP\Util::addStyle('files', 'files'); | |||
OCP\Util::addStyle('files', 'upload'); | |||
OCP\Util::addScript('files', 'filesummary'); | |||
OCP\Util::addScript('files', 'breadcrumb'); | |||
OCP\Util::addScript('files', 'files'); | |||
OCP\Util::addScript('files', 'filelist'); |
@@ -11,6 +11,7 @@ $tmpl = new OCP\Template('files_trashbin', 'index', 'user'); | |||
OCP\Util::addStyle('files', 'files'); | |||
OCP\Util::addStyle('files_trashbin', 'trash'); | |||
OCP\Util::addScript('files', 'filesummary'); | |||
OCP\Util::addScript('files', 'breadcrumb'); | |||
OCP\Util::addScript('files', 'filelist'); | |||
// filelist overrides |
@@ -49,8 +49,8 @@ | |||
} | |||
}; | |||
var oldAdd = FileList.add; | |||
FileList.add = function(fileData, options) { | |||
var oldRenderRow = FileList._renderRow; | |||
FileList._renderRow = function(fileData, options) { | |||
options = options || {}; | |||
var dir = FileList.getCurrentDirectory(); | |||
var dirListing = dir !== '' && dir !== '/'; | |||
@@ -62,7 +62,7 @@ | |||
fileData.displayName = fileData.name; | |||
fileData.name = fileData.name + '.d' + Math.floor(fileData.mtime / 1000); | |||
} | |||
return oldAdd.call(this, fileData, options); | |||
return oldRenderRow.call(this, fileData, options); | |||
}; | |||
FileList.linkTo = function(dir){ | |||
@@ -75,4 +75,130 @@ | |||
$('#emptycontent').toggleClass('hidden', exists); | |||
$('#filestable th').toggleClass('hidden', !exists); | |||
}; | |||
var oldInit = FileList.initialize; | |||
FileList.initialize = function() { | |||
var result = oldInit.apply(this, arguments); | |||
$('.undelete').click('click', FileList._onClickRestoreSelected); | |||
return result; | |||
}; | |||
FileList._removeCallback = function(result) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
var files = result.data.success; | |||
var $el; | |||
for (var i = 0; i < files.length; i++) { | |||
$el = FileList.remove(OC.basename(files[i].filename), {updateSummary: false}); | |||
FileList.fileSummary.remove({type: $el.attr('data-type'), size: $el.attr('data-size')}); | |||
} | |||
FileList.fileSummary.update(); | |||
FileList.updateEmptyContent(); | |||
enableActions(); | |||
} | |||
FileList._onClickRestoreSelected = function(event) { | |||
event.preventDefault(); | |||
var allFiles = $('#select_all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
disableActions(); | |||
if (allFiles) { | |||
FileList.showMask(); | |||
params = { | |||
allfiles: true, | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = _.pluck(FileList.getSelectedFiles(), 'name'); | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('delete-icon').addClass('progress-icon'); | |||
} | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
FileList.hideMask(); | |||
// simply remove all files | |||
FileList.setFiles([]); | |||
enableActions(); | |||
} | |||
else { | |||
FileList._removeCallback(result); | |||
} | |||
} | |||
); | |||
}; | |||
FileList._onClickDeleteSelected = function(event) { | |||
event.preventDefault(); | |||
var allFiles = $('#select_all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
if (allFiles) { | |||
params = { | |||
allfiles: true, | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = _.pluck(FileList.getSelectedFiles(), 'name'); | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
disableActions(); | |||
if (allFiles) { | |||
FileList.showMask(); | |||
} | |||
else { | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('delete-icon').addClass('progress-icon'); | |||
} | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
FileList.hideMask(); | |||
// simply remove all files | |||
FileList.setFiles([]); | |||
enableActions(); | |||
} | |||
else { | |||
FileList._removeCallback(result); | |||
} | |||
} | |||
); | |||
}; | |||
var oldClickFile = FileList._onClickFile; | |||
FileList._onClickFile = function(event) { | |||
var mime = $(this).parent().parent().data('mime'); | |||
if (mime !== 'httpd/unix-directory') { | |||
event.preventDefault(); | |||
} | |||
return oldClickFile.apply(this, arguments); | |||
}; | |||
})(); |
@@ -28,20 +28,6 @@ $(document).ready(function() { | |||
return name; | |||
} | |||
function removeCallback(result) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
var files = result.data.success; | |||
for (var i = 0; i < files.length; i++) { | |||
FileList.remove(OC.basename(files[i].filename), {updateSummary: false}); | |||
} | |||
FileList.updateFileSummary(); | |||
FileList.updateEmptyContent(); | |||
enableActions(); | |||
} | |||
Files.updateStorageStatistics = function() { | |||
// no op because the trashbin doesn't have | |||
// storage info like free space / used space | |||
@@ -57,7 +43,7 @@ $(document).ready(function() { | |||
files: JSON.stringify([filename]), | |||
dir: FileList.getCurrentDirectory() | |||
}, | |||
removeCallback | |||
FileList._removeCallback | |||
); | |||
}, t('files_trashbin', 'Restore')); | |||
}; | |||
@@ -74,153 +60,10 @@ $(document).ready(function() { | |||
files: JSON.stringify([filename]), | |||
dir: FileList.getCurrentDirectory() | |||
}, | |||
removeCallback | |||
FileList._removeCallback | |||
); | |||
}); | |||
// Sets the select_all checkbox behaviour : | |||
$('#select_all').click(function() { | |||
if ($(this).attr('checked')) { | |||
// Check all | |||
$('td.filename input:checkbox').attr('checked', true); | |||
$('td.filename input:checkbox').parent().parent().addClass('selected'); | |||
} else { | |||
// Uncheck all | |||
$('td.filename input:checkbox').attr('checked', false); | |||
$('td.filename input:checkbox').parent().parent().removeClass('selected'); | |||
} | |||
procesSelection(); | |||
}); | |||
$('.undelete').click('click', function(event) { | |||
event.preventDefault(); | |||
var allFiles = $('#select_all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
disableActions(); | |||
if (allFiles) { | |||
FileList.showMask(); | |||
params = { | |||
allfiles: true, | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = Files.getSelectedFiles('name'); | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('delete-icon').addClass('progress-icon'); | |||
} | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
FileList.hideMask(); | |||
// simply remove all files | |||
FileList.update(''); | |||
enableActions(); | |||
} | |||
else { | |||
removeCallback(result); | |||
} | |||
} | |||
); | |||
}); | |||
$('.delete').click('click', function(event) { | |||
event.preventDefault(); | |||
var allFiles = $('#select_all').is(':checked'); | |||
var files = []; | |||
var params = {}; | |||
if (allFiles) { | |||
params = { | |||
allfiles: true, | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
else { | |||
files = Files.getSelectedFiles('name'); | |||
params = { | |||
files: JSON.stringify(files), | |||
dir: FileList.getCurrentDirectory() | |||
}; | |||
} | |||
disableActions(); | |||
if (allFiles) { | |||
FileList.showMask(); | |||
} | |||
else { | |||
for (var i = 0; i < files.length; i++) { | |||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete"); | |||
deleteAction.removeClass('delete-icon').addClass('progress-icon'); | |||
} | |||
} | |||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'), | |||
params, | |||
function(result) { | |||
if (allFiles) { | |||
if (result.status !== 'success') { | |||
OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); | |||
} | |||
FileList.hideMask(); | |||
// simply remove all files | |||
FileList.setFiles([]); | |||
enableActions(); | |||
} | |||
else { | |||
removeCallback(result); | |||
} | |||
} | |||
); | |||
}); | |||
$('#fileList').on('click', 'td.filename input', function() { | |||
var checkbox = $(this).parent().children('input:checkbox'); | |||
$(checkbox).parent().parent().toggleClass('selected'); | |||
if ($(checkbox).is(':checked')) { | |||
var selectedCount = $('td.filename input:checkbox:checked').length; | |||
if (selectedCount === $('td.filename input:checkbox').length) { | |||
$('#select_all').prop('checked', true); | |||
} | |||
} else { | |||
$('#select_all').prop('checked',false); | |||
} | |||
procesSelection(); | |||
}); | |||
$('#fileList').on('click', 'td.filename a', function(event) { | |||
var mime = $(this).parent().parent().data('mime'); | |||
if (mime !== 'httpd/unix-directory') { | |||
event.preventDefault(); | |||
} | |||
var filename = $(this).parent().parent().attr('data-file'); | |||
var tr = FileList.findFileEl(filename); | |||
var renaming = tr.data('renaming'); | |||
if(!renaming){ | |||
if(mime.substr(0, 5) === 'text/'){ //no texteditor for now | |||
return; | |||
} | |||
var type = $(this).parent().parent().data('type'); | |||
var permissions = $(this).parent().parent().data('permissions'); | |||
var action = FileActions.getDefault(mime, type, permissions); | |||
if(action){ | |||
event.preventDefault(); | |||
action(filename); | |||
} | |||
} | |||
}); | |||
/** | |||
* Override crumb URL maker (hacky!) | |||
*/ |
@@ -29,7 +29,7 @@ | |||
<th id="headerDate"> | |||
<span id="modified"><?php p($l->t( 'Deleted' )); ?></span> | |||
<span class="selectedActions"> | |||
<a href="" class="delete"> | |||
<a href="" class="delete-selected"> | |||
<?php p($l->t('Delete'))?> | |||
<img class="svg" alt="<?php p($l->t('Delete'))?>" | |||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" /> | |||
@@ -40,4 +40,6 @@ | |||
</thead> | |||
<tbody id="fileList"> | |||
</tbody> | |||
<tfoot> | |||
</tfoot> | |||
</table> |
@@ -243,7 +243,6 @@ button.loading { | |||
padding-right: 30px; | |||
} | |||
/* general styles for the content area */ | |||
.section { | |||
display: block; | |||
@@ -264,3 +263,14 @@ button.loading { | |||
vertical-align: -2px; | |||
margin-right: 4px; | |||
} | |||
.appear { | |||
opacity: 1; | |||
transition: opacity 500ms ease 0s; | |||
-moz-transition: opacity 500ms ease 0s; | |||
-ms-transition: opacity 500ms ease 0s; | |||
-o-transition: opacity 500ms ease 0s; | |||
-webkit-transition: opacity 500ms ease 0s; | |||
} | |||
.appear.transparent { | |||
opacity: 0; | |||
} |
@@ -1250,9 +1250,12 @@ function relative_modified_date(timestamp) { | |||
} | |||
/** | |||
* @todo Write documentation | |||
* Utility functions | |||
*/ | |||
OC.Util = { | |||
// TODO: remove original functions from global namespace | |||
humanFileSize: humanFileSize, | |||
formatDate: formatDate, | |||
/** | |||
* Returns whether the browser supports SVG | |||
* @return {boolean} true if the browser supports SVG, false otherwise |
@@ -474,5 +474,22 @@ describe('Core base tests', function() { | |||
); | |||
}); | |||
}); | |||
describe('Util', function() { | |||
describe('humanFileSize', function() { | |||
it('renders file sizes with the correct unit', function() { | |||
var data = [ | |||
[0, '0 B'], | |||
[125, '125 B'], | |||
[128000, '125 kB'], | |||
[128000000, '122.1 MB'], | |||
[128000000000, '119.2 GB'], | |||
[128000000000000, '116.4 TB'] | |||
]; | |||
for (var i = 0; i < data.length; i++) { | |||
expect(OC.Util.humanFileSize(data[i][0])).toEqual(data[i][1]); | |||
} | |||
}); | |||
}); | |||
}); | |||
}); | |||