- moved file selection code to FileList - fix selection summary when all files are selected - nextPage now auto-selects files if "select all" checkbox is checked - fixed trashbin to use the same selection logic as FileListtags/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; |
@@ -9,7 +9,7 @@ | |||
*/ | |||
/* global OC, t, n, FileList, FileActions, Files, FileSummary, BreadCrumb */ | |||
/* global procesSelection, dragOptions, folderDropOptions */ | |||
/* global dragOptions, folderDropOptions */ | |||
window.FileList = { | |||
appName: t('files', 'Files'), | |||
isEmpty: true, | |||
@@ -60,6 +60,142 @@ window.FileList = { | |||
var width = $(this).width(); | |||
FileList.breadcrumb.resize(width, false); | |||
}); | |||
this.$fileList.on('click','td.filename a', this._onClickFile); | |||
this.$fileList.on('change', 'td.filename input:checkbox', this._onClickFileCheckbox); | |||
this.$el.find('#select_all').click(this._onClickSelectAll); | |||
this.$el.find('.download').click(this._onClickDownloadSelected); | |||
this.$el.find('.delete-selected').click(this._onClickDeleteSelected); | |||
}, | |||
/** | |||
* Event handler for when clicking on files to select them | |||
*/ | |||
_onClickFile: function(event) { | |||
if (event.ctrlKey || event.shiftKey) { | |||
event.preventDefault(); | |||
if (event.shiftKey) { | |||
var last = $(FileList._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'); | |||
FileList._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'); | |||
} | |||
} | |||
FileList.updateSelectionSummary(); | |||
} 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); | |||
} | |||
} | |||
} | |||
}, | |||
/** | |||
* Event handler for when clicking on a file's checkbox | |||
*/ | |||
_onClickFileCheckbox: function(event) { | |||
// FIXME: not sure what the difference is supposed to be with FileList._onClickFile | |||
if (event.shiftKey) { | |||
var last = $(FileList._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); | |||
} | |||
} | |||
FileList.updateSelectionSummary(); | |||
}, | |||
/** | |||
* Event handler for when selecting/deselecting all files | |||
*/ | |||
_onClickSelectAll: function(e) { | |||
var checked = $(this).prop('checked'); | |||
FileList.$fileList.find('td.filename input:checkbox').prop('checked', checked) | |||
.parent().parent().toggleClass('selected', checked); | |||
FileList.updateSelectionSummary(); | |||
}, | |||
/** | |||
* Event handler for when clicking on "Download" for the selected files | |||
*/ | |||
_onClickDownloadSelected: function(event) { | |||
var files; | |||
var dir = FileList.getCurrentDirectory(); | |||
if (FileList.isAllSelected()) { | |||
files = OC.basename(dir); | |||
dir = OC.dirname(dir) || '/'; | |||
} | |||
else { | |||
files = FileList.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; | |||
}, | |||
/** | |||
* Event handler for when clicking on "Delete" for the selected files | |||
*/ | |||
_onClickDeleteSelected: function(event) { | |||
var files = null; | |||
if (!FileList.isAllSelected()) { | |||
files = FileList.getSelectedFiles('name'); | |||
} | |||
FileList.do_delete(files); | |||
event.preventDefault(); | |||
return false; | |||
}, | |||
/** | |||
@@ -79,7 +215,7 @@ window.FileList = { | |||
if (this.pageNumber + 1 >= this.totalPages) { | |||
return; | |||
} | |||
if ($(window).scrollTop() + $(window).height() > $(document).height() - 20) { | |||
if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) { | |||
this._nextPage(true); | |||
} | |||
}, | |||
@@ -113,7 +249,7 @@ window.FileList = { | |||
if (result) { | |||
if (result.status === 'success') { | |||
FileList.remove(file); | |||
procesSelection(); | |||
FileList.updateSelectionSummary(); | |||
$('#notification').hide(); | |||
} else { | |||
$('#notification').hide(); | |||
@@ -152,13 +288,30 @@ window.FileList = { | |||
return this.$fileList.find('tr').filterAttr('data-file', fileName); | |||
}, | |||
/** | |||
* Returns the file data from a given file element. | |||
* @param $el file tr element | |||
* @return file data | |||
*/ | |||
elementToFile: function($el){ | |||
return { | |||
id: parseInt($el.attr('data-id'), 10), | |||
name: $el.attr('data-file'), | |||
mimetype: $el.attr('data-mime'), | |||
type: $el.attr('data-type'), | |||
size: parseInt($el.attr('data-size'), 10), | |||
etag: $el.attr('data-etag'), | |||
}; | |||
}, | |||
/** | |||
* Appends the next page of files into the table | |||
* @param animate true to animate the new elements | |||
*/ | |||
_nextPage: function(animate) { | |||
var tr, index, count = this.pageSize, | |||
newTrs = []; | |||
newTrs = [], | |||
selected = this.isAllSelected(); | |||
if (this.pageNumber + 1 >= this.totalPages) { | |||
return; | |||
@@ -169,6 +322,10 @@ window.FileList = { | |||
while (count > 0 && index < this.files.length) { | |||
tr = this.add(this.files[index], {updateSummary: false}); | |||
if (selected) { | |||
tr.addClass('selected'); | |||
tr.find('input:checkbox').prop('checked', true); | |||
} | |||
if (animate) { | |||
tr.addClass('appear transparent'); // TODO | |||
newTrs.push(tr); | |||
@@ -201,6 +358,9 @@ window.FileList = { | |||
this.$fileList.detach(); | |||
this.$fileList.empty(); | |||
// clear "Select all" checkbox | |||
$('#select_all').prop('checked', false); | |||
this.isEmpty = this.files.length === 0; | |||
this._nextPage(); | |||
@@ -215,7 +375,7 @@ window.FileList = { | |||
this.fileSummary.calculate(filesArray); | |||
procesSelection(); | |||
FileList.updateSelectionSummary(); | |||
$(window).scrollTop(0); | |||
this.$fileList.trigger(jQuery.Event("updated")); | |||
@@ -580,10 +740,14 @@ window.FileList = { | |||
* @param name name of the file to remove | |||
* @param options optional options as map: | |||
* "updateSummary": true to update the summary (default), false otherwise | |||
* @return deleted element | |||
*/ | |||
remove:function(name, options){ | |||
options = options || {}; | |||
var fileEl = FileList.findFileEl(name); | |||
if (!fileEl.length) { | |||
return null; | |||
} | |||
if (fileEl.data('permissions') & OC.PERMISSION_DELETE) { | |||
// file is only draggable when delete permissions are set | |||
fileEl.find('td.filename').draggable('destroy'); | |||
@@ -824,21 +988,22 @@ window.FileList = { | |||
function(result) { | |||
if (result.status === 'success') { | |||
if (params.allfiles) { | |||
// clear whole list | |||
$('#fileList tr').remove(); | |||
FileList.setFiles([]); | |||
} | |||
else { | |||
$.each(files,function(index,file) { | |||
var fileEl = FileList.remove(file, {updateSummary: false}); | |||
// FIXME: not sure why we need this after the | |||
// element isn't even in the DOM any more | |||
fileEl.find('input[type="checkbox"]').prop('checked', false); | |||
fileEl.removeClass('selected'); | |||
FileList.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')}); | |||
}); | |||
} | |||
procesSelection(); | |||
checkTrashStatus(); | |||
FileList.updateEmptyContent(); | |||
FileList.fileSummary.update(); | |||
FileList.updateSelectionSummary(); | |||
Files.updateStorageStatistics(); | |||
} else { | |||
if (result.status === 'error' && result.data.message) { | |||
@@ -941,14 +1106,95 @@ window.FileList = { | |||
$(e).removeClass("searchresult"); | |||
}); | |||
}, | |||
/** | |||
* Update UI based on the current selection | |||
*/ | |||
updateSelectionSummary: function() { | |||
var allSelected = this.isAllSelected(); | |||
var selected; | |||
var summary = { | |||
totalFiles: 0, | |||
totalDirs: 0, | |||
totalSize: 0 | |||
}; | |||
if (allSelected) { | |||
summary = this.fileSummary.summary; | |||
} | |||
else { | |||
selected = this.getSelectedFiles(); | |||
for (var i = 0; i < selected.length; i++ ){ | |||
if (selected[i].type === 'dir') { | |||
summary.totalDirs++; | |||
} | |||
else { | |||
summary.totalFiles++; | |||
} | |||
summary.totalSize += parseInt(selected[i].size, 10) || 0; | |||
} | |||
} | |||
if (summary.totalFiles === 0 && summary.totalDirs === 0) { | |||
$('#headerName span.name').text(t('files','Name')); | |||
$('#headerSize').text(t('files','Size')); | |||
$('#modified').text(t('files','Modified')); | |||
$('table').removeClass('multiselect'); | |||
$('.selectedActions').addClass('hidden'); | |||
$('#select_all').removeAttr('checked'); | |||
} | |||
else { | |||
$('.selectedActions').removeClass('hidden'); | |||
$('#headerSize').text(humanFileSize(summary.totalSize)); | |||
var selection = ''; | |||
if (summary.totalDirs > 0) { | |||
selection += n('files', '%n folder', '%n folders', summary.totalDirs); | |||
if (summary.totalFiles > 0) { | |||
selection += ' & '; | |||
} | |||
} | |||
if (summary.totalFiles > 0) { | |||
selection += n('files', '%n file', '%n files', summary.totalFiles); | |||
} | |||
$('#headerName span.name').text(selection); | |||
$('#modified').text(''); | |||
$('table').addClass('multiselect'); | |||
} | |||
}, | |||
/** | |||
* Returns whether all files are selected | |||
* @return true if all files are selected, false otherwise | |||
*/ | |||
isAllSelected: function() { | |||
return $('#select_all').prop('checked'); | |||
return this.$el.find('#select_all').prop('checked'); | |||
}, | |||
/** | |||
* @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 | |||
*/ | |||
getSelectedFiles: function(property) { | |||
var elements=$('td.filename input:checkbox:checked').parent().parent(); | |||
var files=[]; | |||
elements.each(function(i,element) { | |||
// TODO: make the json format the same as in FileList.add() | |||
var file = FileList.elementToFile($(element)); | |||
// FIXME: legacy attributes | |||
file.origin = file.id; | |||
file.mime = file.mimetype; | |||
if (property) { | |||
files.push(file[property]); | |||
} else { | |||
files.push(file); | |||
} | |||
}); | |||
return files; | |||
} | |||
}; | |||
} | |||
$(document).ready(function() { | |||
FileList.initialize(); |
@@ -209,7 +209,7 @@ $(document).ready(function() { | |||
// Trigger cancelling of file upload | |||
$('#uploadprogresswrapper .stop').on('click', function() { | |||
OC.Upload.cancelUploads(); | |||
procesSelection(); | |||
FileList.updateSelectionSummary(); | |||
}); | |||
// Show trash bin | |||
@@ -217,130 +217,6 @@ $(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) { | |||
@@ -440,7 +316,7 @@ var createDragShadow = function(event) { | |||
$(event.target).parents('tr').find('td input:first').prop('checked',true); | |||
} | |||
var selectedFiles = Files.getSelectedFiles(); | |||
var selectedFiles = FileList.getSelectedFiles(); | |||
if (!isDragSelected && selectedFiles.length === 1) { | |||
//revert the selection | |||
@@ -539,7 +415,7 @@ var folderDropOptions={ | |||
oldFile.find('td.filesize').text(humanFileSize(newSize)); | |||
FileList.remove(file); | |||
procesSelection(); | |||
FileList.updateSelectionSummary(); | |||
$('#notification').hide(); | |||
} else { | |||
$('#notification').hide(); | |||
@@ -556,78 +432,6 @@ var folderDropOptions={ | |||
tolerance: 'pointer' | |||
}; | |||
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 += ' & '; | |||
} | |||
} | |||
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; | |||
} | |||
Files.getMimeIcon = function(mime, ready) { | |||
if (Files.getMimeIcon.cache[mime]) { | |||
ready(Files.getMimeIcon.cache[mime]); |
@@ -48,8 +48,15 @@ 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>' + | |||
@@ -61,25 +68,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(); | |||
@@ -380,7 +391,7 @@ describe('FileList tests', function() { | |||
$input.val('One_renamed.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'}); | |||
@@ -519,6 +530,16 @@ 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('file previews', function() { | |||
var previewLoadStub; | |||
@@ -811,4 +832,188 @@ 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('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('Clicking "select all" will select/deselect all files', function() { | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(true); | |||
$('#fileList tr input:checkbox').each(function() { | |||
expect($(this).prop('checked')).toEqual(true); | |||
}); | |||
$('#select_all').click(); | |||
expect($('#select_all').prop('checked')).toEqual(false); | |||
$('#fileList tr input:checkbox').each(function() { | |||
expect($(this).prop('checked')).toEqual(false); | |||
}); | |||
}); | |||
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); | |||
}); | |||
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); | |||
}); | |||
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 files', function() { | |||
var files = FileList.getSelectedFiles(); | |||
expect(files.length).toEqual(3); | |||
expect(files[0]).toEqual({ | |||
id: 1, | |||
name: 'One.txt', | |||
mime: 'text/plain', | |||
mimetype: 'text/plain', | |||
type: 'file', | |||
size: 12, | |||
etag: 'abc', | |||
origin: 1 | |||
}); | |||
expect(files[1]).toEqual({ | |||
id: 3, | |||
type: 'file', | |||
name: 'Three.pdf', | |||
mime: 'application/pdf', | |||
mimetype: 'application/pdf', | |||
size: 58009, | |||
etag: '123', | |||
origin: 3 | |||
}); | |||
expect(files[2]).toEqual({ | |||
id: 4, | |||
type: 'dir', | |||
name: 'somedir', | |||
mime: 'httpd/unix-directory', | |||
mimetype: 'httpd/unix-directory', | |||
size: 250, | |||
etag: '456', | |||
origin: 4 | |||
}); | |||
}); | |||
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'); | |||
}); | |||
}); | |||
}); |
@@ -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 = 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.update(''); | |||
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 = 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,22 +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; | |||
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(); | |||
} | |||
Files.updateStorageStatistics = function() { | |||
// no op because the trashbin doesn't have | |||
// storage info like free space / used space | |||
@@ -59,7 +43,7 @@ $(document).ready(function() { | |||
files: JSON.stringify([filename]), | |||
dir: FileList.getCurrentDirectory() | |||
}, | |||
removeCallback | |||
FileList._removeCallback | |||
); | |||
}, t('files_trashbin', 'Restore')); | |||
}; | |||
@@ -76,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!) | |||
*/ |
@@ -1250,7 +1250,7 @@ function relative_modified_date(timestamp) { | |||
} | |||
/** | |||
* @todo Write documentation | |||
* Utility functions | |||
*/ | |||
OC.Util = { | |||
// TODO: remove original functions from global namespace |