diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index aa85644cefb0048feac57085c0a74487802aac0a..b7b8f6fdeb6a0897a3435bab371cc373b365c68f 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -177,549 +177,556 @@ OC.Upload = {
 	checkExistingFiles: function (selection, callbacks) {
 		// TODO check filelist before uploading and show dialog on conflicts, use callbacks
 		callbacks.onNoConflicts(selection);
-	}
-};
-
-$(document).ready(function() {
-
-	if ( $('#file_upload_start').exists() ) {
-
-		var file_upload_param = {
-			dropZone: $('#content'), // restrict dropZone to content div
-			autoUpload: false,
-			sequentialUploads: true,
-			//singleFileUploads is on by default, so the data.files array will always have length 1
-			/**
-			 * on first add of every selection
-			 * - check all files of originalFiles array with files in dir
-			 * - on conflict show dialog
-			 *   - skip all -> remember as single skip action for all conflicting files
-			 *   - replace all -> remember as single replace action for all conflicting files
-			 *   - choose -> show choose dialog
-			 *     - mark files to keep
-			 *       - when only existing -> remember as single skip action
-			 *       - when only new -> remember as single replace action
-			 *       - when both -> remember as single autorename action
-			 * - start uploading selection
-			 * @param {object} e
-			 * @param {object} data
-			 * @returns {boolean}
-			 */
-			add: function(e, data) {
-				OC.Upload.log('add', e, data);
-				var that = $(this);
-
-				// we need to collect all data upload objects before starting the upload so we can check their existence
-				// and set individual conflict actions. unfortunately there is only one variable that we can use to identify
-				// the selection a data upload is part of, so we have to collect them in data.originalFiles
-				// turning singleFileUploads off is not an option because we want to gracefully handle server errors like
-				// already exists
-
-				// create a container where we can store the data objects
-				if ( ! data.originalFiles.selection ) {
-					// initialize selection and remember number of files to upload
-					data.originalFiles.selection = {
-						uploads: [],
-						filesToUpload: data.originalFiles.length,
-						totalBytes: 0
-					};
-				}
-				var selection = data.originalFiles.selection;
-
-				// add uploads
-				if ( selection.uploads.length < selection.filesToUpload ) {
-					// remember upload
-					selection.uploads.push(data);
-				}
-
-				//examine file
-				var file = data.files[0];
-				try {
-					// FIXME: not so elegant... need to refactor that method to return a value
-					Files.isFileNameValid(file.name, FileList.getCurrentDirectory());
-				}
-				catch (errorMessage) {
-					data.textStatus = 'invalidcharacters';
-					data.errorThrown = errorMessage;
-				}
+	},
 
-				if (file.type === '' && file.size === 4096) {
-					data.textStatus = 'dirorzero';
-					data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes',
-						{filename: file.name}
-					);
-				}
+	init: function() {
+		if ( $('#file_upload_start').exists() ) {
+
+			var file_upload_param = {
+				dropZone: $('#content'), // restrict dropZone to content div
+				autoUpload: false,
+				sequentialUploads: true,
+				//singleFileUploads is on by default, so the data.files array will always have length 1
+				/**
+				 * on first add of every selection
+				 * - check all files of originalFiles array with files in dir
+				 * - on conflict show dialog
+				 *   - skip all -> remember as single skip action for all conflicting files
+				 *   - replace all -> remember as single replace action for all conflicting files
+				 *   - choose -> show choose dialog
+				 *     - mark files to keep
+				 *       - when only existing -> remember as single skip action
+				 *       - when only new -> remember as single replace action
+				 *       - when both -> remember as single autorename action
+				 * - start uploading selection
+				 * @param {object} e
+				 * @param {object} data
+				 * @returns {boolean}
+				 */
+				add: function(e, data) {
+					OC.Upload.log('add', e, data);
+					var that = $(this);
+					var freeSpace;
+
+					// we need to collect all data upload objects before starting the upload so we can check their existence
+					// and set individual conflict actions. unfortunately there is only one variable that we can use to identify
+					// the selection a data upload is part of, so we have to collect them in data.originalFiles
+					// turning singleFileUploads off is not an option because we want to gracefully handle server errors like
+					// already exists
+
+					// create a container where we can store the data objects
+					if ( ! data.originalFiles.selection ) {
+						// initialize selection and remember number of files to upload
+						data.originalFiles.selection = {
+							uploads: [],
+							filesToUpload: data.originalFiles.length,
+							totalBytes: 0
+						};
+					}
+					var selection = data.originalFiles.selection;
 
-				// add size
-				selection.totalBytes += file.size;
+					// add uploads
+					if ( selection.uploads.length < selection.filesToUpload ) {
+						// remember upload
+						selection.uploads.push(data);
+					}
 
-				// check PHP upload limit
-				if (selection.totalBytes > $('#upload_limit').val()) {
-					data.textStatus = 'sizeexceedlimit';
-					data.errorThrown = t('files', 'Total file size {size1} exceeds upload limit {size2}', {
-						'size1': humanFileSize(selection.totalBytes),
-						'size2': humanFileSize($('#upload_limit').val())
-					});
-				}
+					//examine file
+					var file = data.files[0];
+					try {
+						// FIXME: not so elegant... need to refactor that method to return a value
+						Files.isFileNameValid(file.name, FileList.getCurrentDirectory());
+					}
+					catch (errorMessage) {
+						data.textStatus = 'invalidcharacters';
+						data.errorThrown = errorMessage;
+					}
 
-				// check free space
-				if (selection.totalBytes > $('#free_space').val()) {
-					data.textStatus = 'notenoughspace';
-					data.errorThrown = t('files', 'Not enough free space, you are uploading {size1} but only {size2} is left', {
-						'size1': humanFileSize(selection.totalBytes),
-						'size2': humanFileSize($('#free_space').val())
-					});
-				}
+					if (file.type === '' && file.size === 4096) {
+						data.textStatus = 'dirorzero';
+						data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes',
+							{filename: file.name}
+						);
+					}
 
-				// end upload for whole selection on error
-				if (data.errorThrown) {
-					// trigger fileupload fail
-					var fu = that.data('blueimp-fileupload') || that.data('fileupload');
-					fu._trigger('fail', e, data);
-					return false; //don't upload anything
-				}
+					// add size
+					selection.totalBytes += file.size;
 
-				// check existing files when all is collected
-				if ( selection.uploads.length >= selection.filesToUpload ) {
+					// check PHP upload limit
+					if (selection.totalBytes > $('#upload_limit').val()) {
+						data.textStatus = 'sizeexceedlimit';
+						data.errorThrown = t('files', 'Total file size {size1} exceeds upload limit {size2}', {
+							'size1': humanFileSize(selection.totalBytes),
+							'size2': humanFileSize($('#upload_limit').val())
+						});
+					}
 
-					//remove our selection hack:
-					delete data.originalFiles.selection;
+					// check free space
+					freeSpace = $('#free_space').val();
+					if (freeSpace >= 0 && selection.totalBytes > freeSpace) {
+						data.textStatus = 'notenoughspace';
+						data.errorThrown = t('files', 'Not enough free space, you are uploading {size1} but only {size2} is left', {
+							'size1': humanFileSize(selection.totalBytes),
+							'size2': humanFileSize($('#free_space').val())
+						});
+					}
 
-					var callbacks = {
+					// end upload for whole selection on error
+					if (data.errorThrown) {
+						// trigger fileupload fail
+						var fu = that.data('blueimp-fileupload') || that.data('fileupload');
+						fu._trigger('fail', e, data);
+						return false; //don't upload anything
+					}
 
-						onNoConflicts: function (selection) {
-							$.each(selection.uploads, function(i, upload) {
-								upload.submit();
-							});
-						},
-						onSkipConflicts: function (selection) {
-							//TODO mark conflicting files as toskip
-						},
-						onReplaceConflicts: function (selection) {
-							//TODO mark conflicting files as toreplace
-						},
-						onChooseConflicts: function (selection) {
-							//TODO mark conflicting files as chosen
-						},
-						onCancel: function (selection) {
-							$.each(selection.uploads, function(i, upload) {
-								upload.abort();
-							});
-						}
-					};
+					// check existing files when all is collected
+					if ( selection.uploads.length >= selection.filesToUpload ) {
+
+						//remove our selection hack:
+						delete data.originalFiles.selection;
+
+						var callbacks = {
+
+							onNoConflicts: function (selection) {
+								$.each(selection.uploads, function(i, upload) {
+									upload.submit();
+								});
+							},
+							onSkipConflicts: function (selection) {
+								//TODO mark conflicting files as toskip
+							},
+							onReplaceConflicts: function (selection) {
+								//TODO mark conflicting files as toreplace
+							},
+							onChooseConflicts: function (selection) {
+								//TODO mark conflicting files as chosen
+							},
+							onCancel: function (selection) {
+								$.each(selection.uploads, function(i, upload) {
+									upload.abort();
+								});
+							}
+						};
 
-					OC.Upload.checkExistingFiles(selection, callbacks);
+						OC.Upload.checkExistingFiles(selection, callbacks);
 
-				}
+					}
 
-				return true; // continue adding files
-			},
-			/**
-			 * called after the first add, does NOT have the data param
-			 * @param {object} e
-			 */
-			start: function(e) {
-				OC.Upload.log('start', e, null);
-			},
-			submit: function(e, data) {
-				OC.Upload.rememberUpload(data);
-				if ( ! data.formData ) {
-					// noone set update parameters, we set the minimum
-					data.formData = {
-						requesttoken: oc_requesttoken,
-								 dir: $('#dir').val()
-					};
-				}
-			},
-			fail: function(e, data) {
-				OC.Upload.log('fail', e, data);
-				if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) {
-					if (data.textStatus === 'abort') {
-						OC.Notification.show(t('files', 'Upload cancelled.'));
-					} else {
-						// HTTP connection problem
-						OC.Notification.show(data.errorThrown);
-						if (data.result) {
-							var result = JSON.parse(data.result);
-							if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') {
-								// abort upload of next files if any
-								OC.Upload.cancelUploads();
+					return true; // continue adding files
+				},
+				/**
+				 * called after the first add, does NOT have the data param
+				 * @param {object} e
+				 */
+				start: function(e) {
+					OC.Upload.log('start', e, null);
+				},
+				submit: function(e, data) {
+					OC.Upload.rememberUpload(data);
+					if ( ! data.formData ) {
+						// noone set update parameters, we set the minimum
+						data.formData = {
+							requesttoken: oc_requesttoken,
+									 dir: $('#dir').val()
+						};
+					}
+				},
+				fail: function(e, data) {
+					OC.Upload.log('fail', e, data);
+					if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) {
+						if (data.textStatus === 'abort') {
+							OC.Notification.show(t('files', 'Upload cancelled.'));
+						} else {
+							// HTTP connection problem
+							OC.Notification.show(data.errorThrown);
+							if (data.result) {
+								var result = JSON.parse(data.result);
+								if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') {
+									// abort upload of next files if any
+									OC.Upload.cancelUploads();
+								}
 							}
 						}
+						//hide notification after 10 sec
+						setTimeout(function() {
+							OC.Notification.hide();
+						}, 10000);
 					}
-					//hide notification after 10 sec
-					setTimeout(function() {
-						OC.Notification.hide();
-					}, 10000);
-				}
-				OC.Upload.deleteUpload(data);
-			},
-			/**
-			 * called for every successful upload
-			 * @param {object} e
-			 * @param {object} data
-			 */
-			done:function(e, data) {
-				OC.Upload.log('done', e, data);
-				// handle different responses (json or body from iframe for ie)
-				var response;
-				if (typeof data.result === 'string') {
-					response = data.result;
-				} else {
-					//fetch response from iframe
-					response = data.result[0].body.innerText;
-				}
-				var result=$.parseJSON(response);
-
-				delete data.jqXHR;
-
-				if (result.status === 'error' && result.data && result.data.message){
-					data.textStatus = 'servererror';
-					data.errorThrown = result.data.message;
-					var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
-					fu._trigger('fail', e, data);
-				} else if (typeof result[0] === 'undefined') {
-					data.textStatus = 'servererror';
-					data.errorThrown = t('files', 'Could not get result from server.');
-					var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
-					fu._trigger('fail', e, data);
-				} else if (result[0].status === 'existserror') {
-					//show "file already exists" dialog
-					var original = result[0];
-					var replacement = data.files[0];
-					var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
-					OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu);
-				} else if (result[0].status !== 'success') {
-					//delete data.jqXHR;
-					data.textStatus = 'servererror';
-					data.errorThrown = result[0].data.message; // error message has been translated on server
-					var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
-					fu._trigger('fail', e, data);
+					OC.Upload.deleteUpload(data);
+				},
+				/**
+				 * called for every successful upload
+				 * @param {object} e
+				 * @param {object} data
+				 */
+				done:function(e, data) {
+					OC.Upload.log('done', e, data);
+					// handle different responses (json or body from iframe for ie)
+					var response;
+					if (typeof data.result === 'string') {
+						response = data.result;
+					} else {
+						//fetch response from iframe
+						response = data.result[0].body.innerText;
+					}
+					var result=$.parseJSON(response);
+
+					delete data.jqXHR;
+
+					if (result.status === 'error' && result.data && result.data.message){
+						data.textStatus = 'servererror';
+						data.errorThrown = result.data.message;
+						var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
+						fu._trigger('fail', e, data);
+					} else if (typeof result[0] === 'undefined') {
+						data.textStatus = 'servererror';
+						data.errorThrown = t('files', 'Could not get result from server.');
+						var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
+						fu._trigger('fail', e, data);
+					} else if (result[0].status === 'existserror') {
+						//show "file already exists" dialog
+						var original = result[0];
+						var replacement = data.files[0];
+						var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
+						OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu);
+					} else if (result[0].status !== 'success') {
+						//delete data.jqXHR;
+						data.textStatus = 'servererror';
+						data.errorThrown = result[0].data.message; // error message has been translated on server
+						var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
+						fu._trigger('fail', e, data);
+					}
+				},
+				/**
+				 * called after last upload
+				 * @param {object} e
+				 * @param {object} data
+				 */
+				stop: function(e, data) {
+					OC.Upload.log('stop', e, data);
 				}
-			},
-			/**
-			 * called after last upload
-			 * @param {object} e
-			 * @param {object} data
-			 */
-			stop: function(e, data) {
-				OC.Upload.log('stop', e, data);
-			}
-		};
+			};
+
+			// initialize jquery fileupload (blueimp)
+			var fileupload = $('#file_upload_start').fileupload(file_upload_param);
+			window.file_upload_param = fileupload;
+
+			if (supportAjaxUploadWithProgress()) {
+
+				// add progress handlers
+				fileupload.on('fileuploadadd', function(e, data) {
+					OC.Upload.log('progress handle fileuploadadd', e, data);
+					//show cancel button
+					//if (data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie?
+					//	$('#uploadprogresswrapper input.stop').show();
+					//}
+				});
+				// add progress handlers
+				fileupload.on('fileuploadstart', function(e, data) {
+					OC.Upload.log('progress handle fileuploadstart', e, data);
+					$('#uploadprogresswrapper input.stop').show();
+					$('#uploadprogressbar').progressbar({value:0});
+					$('#uploadprogressbar').fadeIn();
+				});
+				fileupload.on('fileuploadprogress', function(e, data) {
+					OC.Upload.log('progress handle fileuploadprogress', e, data);
+					//TODO progressbar in row
+				});
+				fileupload.on('fileuploadprogressall', function(e, data) {
+					OC.Upload.log('progress handle fileuploadprogressall', e, data);
+					var progress = (data.loaded / data.total) * 100;
+					$('#uploadprogressbar').progressbar('value', progress);
+				});
+				fileupload.on('fileuploadstop', function(e, data) {
+					OC.Upload.log('progress handle fileuploadstop', e, data);
 
-		// initialize jquery fileupload (blueimp)
-		var fileupload = $('#file_upload_start').fileupload(file_upload_param);
-		window.file_upload_param = fileupload;
-
-		if (supportAjaxUploadWithProgress()) {
-
-			// add progress handlers
-			fileupload.on('fileuploadadd', function(e, data) {
-				OC.Upload.log('progress handle fileuploadadd', e, data);
-				//show cancel button
-				//if (data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie?
-				//	$('#uploadprogresswrapper input.stop').show();
-				//}
-			});
-			// add progress handlers
-			fileupload.on('fileuploadstart', function(e, data) {
-				OC.Upload.log('progress handle fileuploadstart', e, data);
-				$('#uploadprogresswrapper input.stop').show();
-				$('#uploadprogressbar').progressbar({value:0});
-				$('#uploadprogressbar').fadeIn();
-			});
-			fileupload.on('fileuploadprogress', function(e, data) {
-				OC.Upload.log('progress handle fileuploadprogress', e, data);
-				//TODO progressbar in row
-			});
-			fileupload.on('fileuploadprogressall', function(e, data) {
-				OC.Upload.log('progress handle fileuploadprogressall', e, data);
-				var progress = (data.loaded / data.total) * 100;
-				$('#uploadprogressbar').progressbar('value', progress);
-			});
-			fileupload.on('fileuploadstop', function(e, data) {
-				OC.Upload.log('progress handle fileuploadstop', e, data);
-
-				$('#uploadprogresswrapper input.stop').fadeOut();
-				$('#uploadprogressbar').fadeOut();
-			    Files.updateStorageStatistics();
-			});
-			fileupload.on('fileuploadfail', function(e, data) {
-				OC.Upload.log('progress handle fileuploadfail', e, data);
-				//if user pressed cancel hide upload progress bar and cancel button
-				if (data.errorThrown === 'abort') {
 					$('#uploadprogresswrapper input.stop').fadeOut();
 					$('#uploadprogressbar').fadeOut();
-				}
-			});
-
-		} else {
-			console.log('skipping file progress because your browser is broken');
-		}
-	}
+					Files.updateStorageStatistics();
+				});
+				fileupload.on('fileuploadfail', function(e, data) {
+					OC.Upload.log('progress handle fileuploadfail', e, data);
+					//if user pressed cancel hide upload progress bar and cancel button
+					if (data.errorThrown === 'abort') {
+						$('#uploadprogresswrapper input.stop').fadeOut();
+						$('#uploadprogressbar').fadeOut();
+					}
+				});
 
-	$.assocArraySize = function(obj) {
-		// http://stackoverflow.com/a/6700/11236
-		var size = 0, key;
-		for (key in obj) {
-			if (obj.hasOwnProperty(key)) {
-				size++;
+			} else {
+				console.log('skipping file progress because your browser is broken');
 			}
 		}
-		return size;
-	};
 
-	// warn user not to leave the page while upload is in progress
-	$(window).on('beforeunload', function(e) {
-		if (OC.Upload.isProcessing()) {
-			return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.');
-		}
-	});
+		$.assocArraySize = function(obj) {
+			// http://stackoverflow.com/a/6700/11236
+			var size = 0, key;
+			for (key in obj) {
+				if (obj.hasOwnProperty(key)) {
+					size++;
+				}
+			}
+			return size;
+		};
 
-	//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
-	if (navigator.userAgent.search(/konqueror/i) === -1) {
-		$('#file_upload_start').attr('multiple', 'multiple');
-	}
+		// warn user not to leave the page while upload is in progress
+		$(window).on('beforeunload', function(e) {
+			if (OC.Upload.isProcessing()) {
+				return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.');
+			}
+		});
 
-	//if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder
-	var crumb=$('div.crumb').first();
-	while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) {
-		crumb.children('a').text('...');
-		crumb = crumb.next('div.crumb');
-	}
-	//if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent
-	var crumb = $('div.crumb').first();
-	var next = crumb.next('div.crumb');
-	while($('div.controls').height()>40 && next.next('div.crumb').length > 0) {
-		crumb.remove();
-		crumb = next;
-		next = crumb.next('div.crumb');
-	}
-	//still not enough, start shorting down the current folder name
-	var crumb=$('div.crumb>a').last();
-	while($('div.controls').height() > 40 && crumb.text().length > 6) {
-		var text=crumb.text();
-		text = text.substr(0,text.length-6)+'...';
-		crumb.text(text);
-	}
+		//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
+		if (navigator.userAgent.search(/konqueror/i) === -1) {
+			$('#file_upload_start').attr('multiple', 'multiple');
+		}
 
-	$(document).click(function(ev) {
-		// do not close when clicking in the dropdown
-		if ($(ev.target).closest('#new').length){
-			return;
+		//if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder
+		var crumb=$('div.crumb').first();
+		while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) {
+			crumb.children('a').text('...');
+			crumb = crumb.next('div.crumb');
 		}
-		$('#new>ul').hide();
-		$('#new').removeClass('active');
-		if ($('#new .error').length > 0) {
-			$('#new .error').tipsy('hide');
+		//if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent
+		var crumb = $('div.crumb').first();
+		var next = crumb.next('div.crumb');
+		while($('div.controls').height()>40 && next.next('div.crumb').length > 0) {
+			crumb.remove();
+			crumb = next;
+			next = crumb.next('div.crumb');
 		}
-		$('#new li').each(function(i,element) {
-			if ($(element).children('p').length === 0) {
-				$(element).children('form').remove();
-				$(element).append('<p>'+$(element).data('text')+'</p>');
-			}
-		});
-	});
-	$('#new').click(function(event) {
-		event.stopPropagation();
-	});
-	$('#new>a').click(function() {
-		$('#new>ul').toggle();
-		$('#new').toggleClass('active');
-	});
-	$('#new li').click(function() {
-		if ($(this).children('p').length === 0) {
-			return;
+		//still not enough, start shorting down the current folder name
+		var crumb=$('div.crumb>a').last();
+		while($('div.controls').height() > 40 && crumb.text().length > 6) {
+			var text=crumb.text();
+			text = text.substr(0,text.length-6)+'...';
+			crumb.text(text);
 		}
 
-		$('#new .error').tipsy('hide');
-
-		$('#new li').each(function(i,element) {
-			if ($(element).children('p').length === 0) {
-				$(element).children('form').remove();
-				$(element).append('<p>'+$(element).data('text')+'</p>');
+		$(document).click(function(ev) {
+			// do not close when clicking in the dropdown
+			if ($(ev.target).closest('#new').length){
+				return;
 			}
+			$('#new>ul').hide();
+			$('#new').removeClass('active');
+			if ($('#new .error').length > 0) {
+				$('#new .error').tipsy('hide');
+			}
+			$('#new li').each(function(i,element) {
+				if ($(element).children('p').length === 0) {
+					$(element).children('form').remove();
+					$(element).append('<p>'+$(element).data('text')+'</p>');
+				}
+			});
 		});
-
-		var type=$(this).data('type');
-		var text=$(this).children('p').text();
-		$(this).data('text',text);
-		$(this).children('p').remove();
-
-		// add input field
-		var form = $('<form></form>');
-		var input = $('<input type="text">');
-		var newName = $(this).attr('data-newname') || '';
-		if (newName) {
-			input.val(newName);
-		}
-		form.append(input);
-		$(this).append(form);
-		var lastPos;
-		var checkInput = function () {
-			var filename = input.val();
-			if (type === 'web' && filename.length === 0) {
-				throw t('files', 'URL cannot be empty');
-			} else if (type !== 'web' && !Files.isFileNameValid(filename)) {
-				// Files.isFileNameValid(filename) throws an exception itself
-			} else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') {
-				throw t('files', 'In the home folder \'Shared\' is a reserved filename');
-			} else if (FileList.inList(filename)) {
-				throw t('files', '{new_name} already exists', {new_name: filename});
-			} else {
-				return true;
+		$('#new').click(function(event) {
+			event.stopPropagation();
+		});
+		$('#new>a').click(function() {
+			$('#new>ul').toggle();
+			$('#new').toggleClass('active');
+		});
+		$('#new li').click(function() {
+			if ($(this).children('p').length === 0) {
+				return;
 			}
-		};
 
-		// verify filename on typing
-		input.keyup(function(event) {
-			try {
-				checkInput();
-				input.tipsy('hide');
-				input.removeClass('error');
-			} catch (error) {
-				input.attr('title', error);
-				input.tipsy({gravity: 'w', trigger: 'manual'});
-				input.tipsy('show');
-				input.addClass('error');
-			}
-		});
+			$('#new .error').tipsy('hide');
 
-		input.focus();
-		// pre select name up to the extension
-		lastPos = newName.lastIndexOf('.');
-		if (lastPos === -1) {
-			lastPos = newName.length;
-		}
-		input.selectRange(0, lastPos);
-		form.submit(function(event) {
-			event.stopPropagation();
-			event.preventDefault();
-			try {
-				checkInput();
-				var newname = input.val();
-				if (FileList.lastAction) {
-					FileList.lastAction();
+			$('#new li').each(function(i,element) {
+				if ($(element).children('p').length === 0) {
+					$(element).children('form').remove();
+					$(element).append('<p>'+$(element).data('text')+'</p>');
 				}
-				var name = getUniqueName(newname);
-				if (newname !== name) {
-					FileList.checkName(name, newname, true);
-					var hidden = true;
+			});
+
+			var type=$(this).data('type');
+			var text=$(this).children('p').text();
+			$(this).data('text',text);
+			$(this).children('p').remove();
+
+			// add input field
+			var form = $('<form></form>');
+			var input = $('<input type="text">');
+			var newName = $(this).attr('data-newname') || '';
+			if (newName) {
+				input.val(newName);
+			}
+			form.append(input);
+			$(this).append(form);
+			var lastPos;
+			var checkInput = function () {
+				var filename = input.val();
+				if (type === 'web' && filename.length === 0) {
+					throw t('files', 'URL cannot be empty');
+				} else if (type !== 'web' && !Files.isFileNameValid(filename)) {
+					// Files.isFileNameValid(filename) throws an exception itself
+				} else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') {
+					throw t('files', 'In the home folder \'Shared\' is a reserved filename');
+				} else if (FileList.inList(filename)) {
+					throw t('files', '{new_name} already exists', {new_name: filename});
 				} else {
-					var hidden = false;
+					return true;
 				}
-				switch(type) {
-					case 'file':
-						$.post(
-							OC.filePath('files', 'ajax', 'newfile.php'),
-							{dir:$('#dir').val(), filename:name},
-							function(result) {
-								if (result.status === 'success') {
-									var date = new Date();
-									// TODO: ideally addFile should be able to receive
-									// all attributes and set them automatically,
-									// and also auto-load the preview
-									var tr = FileList.addFile(name, 0, date, false, hidden);
-									tr.attr('data-size', result.data.size);
-									tr.attr('data-mime', result.data.mime);
-									tr.attr('data-id', result.data.id);
-									tr.attr('data-etag', result.data.etag);
-									tr.find('.filesize').text(humanFileSize(result.data.size));
-									var path = getPathForPreview(name);
-									Files.lazyLoadPreview(path, result.data.mime, function(previewpath) {
-										tr.find('td.filename').attr('style','background-image:url('+previewpath+')');
-									}, null, null, result.data.etag);
-									FileActions.display(tr.find('td.filename'), true);
-								} else {
-									OC.dialogs.alert(result.data.message, t('core', 'Could not create file'));
+			};
+
+			// verify filename on typing
+			input.keyup(function(event) {
+				try {
+					checkInput();
+					input.tipsy('hide');
+					input.removeClass('error');
+				} catch (error) {
+					input.attr('title', error);
+					input.tipsy({gravity: 'w', trigger: 'manual'});
+					input.tipsy('show');
+					input.addClass('error');
+				}
+			});
+
+			input.focus();
+			// pre select name up to the extension
+			lastPos = newName.lastIndexOf('.');
+			if (lastPos === -1) {
+				lastPos = newName.length;
+			}
+			input.selectRange(0, lastPos);
+			form.submit(function(event) {
+				event.stopPropagation();
+				event.preventDefault();
+				try {
+					checkInput();
+					var newname = input.val();
+					if (FileList.lastAction) {
+						FileList.lastAction();
+					}
+					var name = getUniqueName(newname);
+					if (newname !== name) {
+						FileList.checkName(name, newname, true);
+						var hidden = true;
+					} else {
+						var hidden = false;
+					}
+					switch(type) {
+						case 'file':
+							$.post(
+								OC.filePath('files', 'ajax', 'newfile.php'),
+								{dir:$('#dir').val(), filename:name},
+								function(result) {
+									if (result.status === 'success') {
+										var date = new Date();
+										// TODO: ideally addFile should be able to receive
+										// all attributes and set them automatically,
+										// and also auto-load the preview
+										var tr = FileList.addFile(name, 0, date, false, hidden);
+										tr.attr('data-size', result.data.size);
+										tr.attr('data-mime', result.data.mime);
+										tr.attr('data-id', result.data.id);
+										tr.attr('data-etag', result.data.etag);
+										tr.find('.filesize').text(humanFileSize(result.data.size));
+										var path = getPathForPreview(name);
+										Files.lazyLoadPreview(path, result.data.mime, function(previewpath) {
+											tr.find('td.filename').attr('style','background-image:url('+previewpath+')');
+										}, null, null, result.data.etag);
+										FileActions.display(tr.find('td.filename'), true);
+									} else {
+										OC.dialogs.alert(result.data.message, t('core', 'Could not create file'));
+									}
 								}
-							}
-						);
-						break;
-					case 'folder':
-						$.post(
-							OC.filePath('files','ajax','newfolder.php'),
-							{dir:$('#dir').val(), foldername:name},
-							function(result) {
-								if (result.status === 'success') {
-									var date=new Date();
-									FileList.addDir(name, 0, date, hidden);
-									var tr = FileList.findFileEl(name);
-									tr.attr('data-id', result.data.id);
-								} else {
-									OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
+							);
+							break;
+						case 'folder':
+							$.post(
+								OC.filePath('files','ajax','newfolder.php'),
+								{dir:$('#dir').val(), foldername:name},
+								function(result) {
+									if (result.status === 'success') {
+										var date=new Date();
+										FileList.addDir(name, 0, date, hidden);
+										var tr = FileList.findFileEl(name);
+										tr.attr('data-id', result.data.id);
+									} else {
+										OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
+									}
 								}
+							);
+							break;
+						case 'web':
+							if (name.substr(0,8) !== 'https://' && name.substr(0,7) !== 'http://') {
+								name = 'http://' + name;
 							}
-						);
-						break;
-					case 'web':
-						if (name.substr(0,8) !== 'https://' && name.substr(0,7) !== 'http://') {
-							name = 'http://' + name;
-						}
-						var localName=name;
-						if (localName.substr(localName.length-1,1)==='/') {//strip /
-							localName=localName.substr(0,localName.length-1);
-						}
-						if (localName.indexOf('/')) {//use last part of url
-							localName=localName.split('/').pop();
-						} else { //or the domain
-							localName=(localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.','');
-						}
-						localName = getUniqueName(localName);
-						//IE < 10 does not fire the necessary events for the progress bar.
-						if ($('html.lte9').length === 0) {
-							$('#uploadprogressbar').progressbar({value:0});
-							$('#uploadprogressbar').fadeIn();
-						}
-
-						var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName});
-						eventSource.listen('progress',function(progress) {
+							var localName=name;
+							if (localName.substr(localName.length-1,1)==='/') {//strip /
+								localName=localName.substr(0,localName.length-1);
+							}
+							if (localName.indexOf('/')) {//use last part of url
+								localName=localName.split('/').pop();
+							} else { //or the domain
+								localName=(localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.','');
+							}
+							localName = getUniqueName(localName);
 							//IE < 10 does not fire the necessary events for the progress bar.
 							if ($('html.lte9').length === 0) {
-								$('#uploadprogressbar').progressbar('value',progress);
+								$('#uploadprogressbar').progressbar({value:0});
+								$('#uploadprogressbar').fadeIn();
 							}
-						});
-						eventSource.listen('success',function(data) {
-							var mime = data.mime;
-							var size = data.size;
-							var id = data.id;
-							$('#uploadprogressbar').fadeOut();
-							var date = new Date();
-							FileList.addFile(localName, size, date, false, hidden);
-							var tr = FileList.findFileEl(localName);
-							tr.data('mime', mime).data('id', id);
-							tr.attr('data-id', id);
-							var path = $('#dir').val()+'/'+localName;
-							Files.lazyLoadPreview(path, mime, function(previewpath) {
-								tr.find('td.filename').attr('style', 'background-image:url('+previewpath+')');
-							}, null, null, data.etag);
-							FileActions.display(tr.find('td.filename'), true);
-						});
-						eventSource.listen('error',function(error) {
-							$('#uploadprogressbar').fadeOut();
-							var message = (error && error.message) || t('core', 'Error fetching URL');
-							OC.Notification.show(message);
-							//hide notification after 10 sec
-							setTimeout(function() {
-								OC.Notification.hide();
-							}, 10000);
-						});
-						break;
+
+							var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName});
+							eventSource.listen('progress',function(progress) {
+								//IE < 10 does not fire the necessary events for the progress bar.
+								if ($('html.lte9').length === 0) {
+									$('#uploadprogressbar').progressbar('value',progress);
+								}
+							});
+							eventSource.listen('success',function(data) {
+								var mime = data.mime;
+								var size = data.size;
+								var id = data.id;
+								$('#uploadprogressbar').fadeOut();
+								var date = new Date();
+								FileList.addFile(localName, size, date, false, hidden);
+								var tr = FileList.findFileEl(localName);
+								tr.data('mime', mime).data('id', id);
+								tr.attr('data-id', id);
+								var path = $('#dir').val()+'/'+localName;
+								Files.lazyLoadPreview(path, mime, function(previewpath) {
+									tr.find('td.filename').attr('style', 'background-image:url('+previewpath+')');
+								}, null, null, data.etag);
+								FileActions.display(tr.find('td.filename'), true);
+							});
+							eventSource.listen('error',function(error) {
+								$('#uploadprogressbar').fadeOut();
+								var message = (error && error.message) || t('core', 'Error fetching URL');
+								OC.Notification.show(message);
+								//hide notification after 10 sec
+								setTimeout(function() {
+									OC.Notification.hide();
+								}, 10000);
+							});
+							break;
+					}
+					var li=form.parent();
+					form.remove();
+					/* workaround for IE 9&10 click event trap, 2 lines: */
+					$('input').first().focus();
+					$('#content').focus();
+					li.append('<p>'+li.data('text')+'</p>');
+					$('#new>a').click();
+				} catch (error) {
+					input.attr('title', error);
+					input.tipsy({gravity: 'w', trigger: 'manual'});
+					input.tipsy('show');
+					input.addClass('error');
 				}
-				var li=form.parent();
-				form.remove();
-				/* workaround for IE 9&10 click event trap, 2 lines: */
-				$('input').first().focus();
-				$('#content').focus();
-				li.append('<p>'+li.data('text')+'</p>');
-				$('#new>a').click();
-			} catch (error) {
-				input.attr('title', error);
-				input.tipsy({gravity: 'w', trigger: 'manual'});
-				input.tipsy('show');
-				input.addClass('error');
-			}
+			});
 		});
-	});
-	window.file_upload_param = file_upload_param;
+		window.file_upload_param = file_upload_param;
+		return file_upload_param;
+	}
+};
+
+$(document).ready(function() {
+	OC.Upload.init();
 });
+
diff --git a/apps/files/tests/js/fileUploadSpec.js b/apps/files/tests/js/fileUploadSpec.js
new file mode 100644
index 0000000000000000000000000000000000000000..2b4341ef1c345ad67d7a0d59126124763c5dd432
--- /dev/null
+++ b/apps/files/tests/js/fileUploadSpec.js
@@ -0,0 +1,127 @@
+/**
+* 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 */
+describe('OC.Upload tests', function() {
+	var $dummyUploader;
+	var testFile;
+
+	beforeEach(function() {
+		testFile = {
+			name: 'test.txt',
+			size: 5000, // 5 KB
+			type: 'text/plain',
+			lastModifiedDate: new Date()
+		};
+		// need a dummy button because file-upload checks on it
+		$('#testArea').append(
+			'<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
+			'<input type="hidden" id="upload_limit" name="upload_limit" value="10000000">' + // 10 MB
+			'<input type="hidden" id="free_space" name="free_space" value="50000000">' // 50 MB
+		);
+		$dummyUploader = $('#file_upload_start');
+	});
+	afterEach(function() {
+		delete window.file_upload_param;
+		$dummyUploader = undefined;
+	});
+	describe('Adding files for upload', function() {
+		var params;
+		var failStub;
+
+		beforeEach(function() {
+			params = OC.Upload.init();
+			failStub = sinon.stub();
+			$dummyUploader.on('fileuploadfail', failStub);
+		});
+		afterEach(function() {
+			params = undefined;
+			failStub = undefined;
+		});
+
+		/**
+		 * Add file for upload
+		 * @param file file data
+		 */
+		function addFile(file) {
+			return params.add.call(
+					$dummyUploader[0],
+					{},
+					{
+					originalFiles: {},
+					files: [file]
+				});
+		}
+
+		it('adds file when size is below limits', function() {
+			var result = addFile(testFile);
+			expect(result).toEqual(true);
+		});
+		it('adds file when free space is unknown', function() {
+			var result;
+			$('#free_space').val(-2);
+
+			result = addFile(testFile);
+
+			expect(result).toEqual(true);
+			expect(failStub.notCalled).toEqual(true);
+		});
+		it('does not add file if it exceeds upload limit', function() {
+			var result;
+			$('#upload_limit').val(1000);
+
+			result = addFile(testFile);
+
+			expect(result).toEqual(false);
+			expect(failStub.calledOnce).toEqual(true);
+			expect(failStub.getCall(0).args[1].textStatus).toEqual('sizeexceedlimit');
+			expect(failStub.getCall(0).args[1].errorThrown).toEqual(
+				'Total file size 5 kB exceeds upload limit 1000 B'
+			);
+		});
+		it('does not add file if it exceeds free space', function() {
+			var result;
+			$('#free_space').val(1000);
+
+			result = addFile(testFile);
+
+			expect(result).toEqual(false);
+			expect(failStub.calledOnce).toEqual(true);
+			expect(failStub.getCall(0).args[1].textStatus).toEqual('notenoughspace');
+			expect(failStub.getCall(0).args[1].errorThrown).toEqual(
+				'Not enough free space, you are uploading 5 kB but only 1000 B is left'
+			);
+		});
+		it('does not add file if it has invalid characters', function() {
+			var result;
+			testFile.name = 'stars*stars.txt';
+
+			result = addFile(testFile);
+
+			expect(result).toEqual(false);
+			expect(failStub.calledOnce).toEqual(true);
+			expect(failStub.getCall(0).args[1].textStatus).toEqual('invalidcharacters');
+			expect(failStub.getCall(0).args[1].errorThrown.substr(0, 12)).toEqual(
+				'Invalid name'
+			);
+		});
+	});
+});
diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js
index 3ed4cb6c3b4e3e91ec3374aae9e90ee6abb5f4ff..d86cd81cda86b58a20c0879b3061c708bf895c22 100644
--- a/core/js/tests/specHelper.js
+++ b/core/js/tests/specHelper.js
@@ -68,9 +68,14 @@ window.oc_defaults = {};
 // global setup for all tests
 (function setupTests() {
 	var fakeServer = null,
+		$testArea = null,
 		routesRequestStub;
 
 	beforeEach(function() {
+		// test area for elements that need absolute selector access or measure widths/heights
+		// which wouldn't work for detached or hidden elements
+		$testArea = $('<div id="testArea" style="position: absolute; width: 1280px; height: 800px; top: -3000px; left: -3000px;"></div>');
+		$('body').append($testArea);
 		// enforce fake XHR, tests should not depend on the server and
 		// must use fake responses for expected calls
 		fakeServer = sinon.fakeServer.create();
@@ -92,6 +97,8 @@ window.oc_defaults = {};
 		// uncomment this to log requests
 		// console.log(window.fakeServer.requests);
 		fakeServer.restore();
+
+		$testArea.remove();
 	});
 })();
 
diff --git a/lib/private/helper.php b/lib/private/helper.php
index d8c4650f6660aa1966505430c0f2bd65701d35c9..b9956d5ec1cd4d3f6ba59a09360cc7ae66f1723b 100644
--- a/lib/private/helper.php
+++ b/lib/private/helper.php
@@ -839,7 +839,7 @@ class OC_Helper {
 	 * @return int number of bytes representing
 	 */
 	public static function maxUploadFilesize($dir, $freeSpace = null) {
-		if (is_null($freeSpace)){
+		if (is_null($freeSpace) || $freeSpace < 0){
 			$freeSpace = self::freeSpace($dir);
 		}
 		return min($freeSpace, self::uploadLimit());