diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 474f1af0720f52fd73b9e1deee3a151825644a49..533050691d5e75b94bce6bcf047eec6828dfa1ea 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -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;
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 223f4bb44098f9d35bacbc7022d6f0a474019bc3..02754d7acbb907517a5be9be637dd2fcd6f08af0 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -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();
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 5e669a796a9efdb95f95f0f69cebb59822408619..6cb0d41a611fecd6d3eeaa021ab61b38b5e42145 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -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]);
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 6e80d78eee049618cbb5a00392c707e387fd8a05..93e7c81cb1f799e9bf21206d5c7bfcf97132694a 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -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);
+				});
+			});
+		});
+	});
 });
diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js
index 018c8ef0f3cff215a718a9491e9fbad12a47c1b2..7f8848619f500c57b795439783d3d29237a3a689 100644
--- a/apps/files/tests/js/filesSpec.js
+++ b/apps/files/tests/js/filesSpec.js
@@ -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');
+		});
+	});
 });
diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js
index 7795daf2775bbc609c82c7dfcac868d565cccc48..6c9b345086a8a731e4a0e2418e895f92c29742d4 100644
--- a/apps/files_trashbin/js/filelist.js
+++ b/apps/files_trashbin/js/filelist.js
@@ -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);
+	};
+
 })();
diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js
index 4ed5ba1c76e22042dce67f89a69e65694ffb40c7..5f2436de8096c78bb7eaffdc50c0dc29f9ee1713 100644
--- a/apps/files_trashbin/js/trash.js
+++ b/apps/files_trashbin/js/trash.js
@@ -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!)
 	 */
diff --git a/core/js/js.js b/core/js/js.js
index 325be6cdc53dd942cdd0faf7092575ec1ac92433..27bc3c651e33125ef27986314b3576df4ac27477 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1250,7 +1250,7 @@ function relative_modified_date(timestamp) {
 }
 
 /**
- *  @todo Write documentation
+ * Utility functions
  */
 OC.Util = {
 	// TODO: remove original functions from global namespace