var FileShareFile = Class.create({

	toggleCancel: function (canCancel) {
		if (canCancel)
			this.cancelEl.show();
		else
			this.cancelEl.hide();
		this.canCancel = canCancel;
	},
	
	setStatus: function (message) {
		this.statusEl.update(message);
		if (message=='Uploading...')
			this.el.removeClassName('pending');
	},
	
	setError: function () {
		this.el.addClassName('error');
		this.statusWrap.hide();
		this.el.removeClassName('uploading');
	},
	setComplete: function () {
		this.statusWrap.hide();
		this.el.removeClassName('uploading');
		fileShare.makeFileNew(this.el);
		fileShare.reloadFile(this.file.name,fileShare.getFolderPath(this.folder),this.el);
	},
	setProgress: function (percent) {
		this.progressEl.update(percent+'%');
		this.progressBar.style.width = percent+'%';
	},
	cancel: function () {
		this.folder.swfupload.cancelUpload(this.file.id);
		var ul = this.el.up('ul');
		this.el.remove();
		fileShare.checkForEmptyFolders(ul);
	},
	
	uploadStarted: false,
	
	/*
		file - data object
		folder	- <ul>
	*/
	initialize: function (file,folder) {
		this.file = file;
		this.folder = folder;
		this.nameEl = new Element('span',{'class':'name'}).update(file.name);
		this.progressEl = new Element('span',{'class':'progress'});
		this.progressBar = new Element('span',{'class':'progressBar'});
		this.statusEl = new Element('span').update('Queued');
		this.statusWrap = new Element('span',{'class':'status'})
							.insert(this.statusEl)
							.insert({bottom:this.progressEl})
							.insert({bottom:this.progressBar});
		this.cancelEl = new Element('span',{'class':'cancel'}).update('Cancel').observe('click',this.cancel.bind(this));
		this.el = new Element('li',{'class':'uploading pending','id':'file-'+file.id})
						.insert({bottom:this.nameEl})
						.insert({top:this.statusWrap})
						.insert({top:this.cancelEl});
		
		var fileext = file.name.match(/\.([a-zA-Z0-9]+)$/);
		if (fileext)
			this.el.addClassName('type-'+fileext[1].toLowerCase());
		
		fileShare.insertInOrder(folder,this.el,file.name);
		/*// insert in correct alphabetical position
		var refLi = folder.childElements().find(function(li){ return (li.name && li.name > file.name); });
		if (!refLi)
			refLi = folder.childElements().last();
		refLi.insert({before:this.el});
		*/
		
		fileShare.checkForEmptyFolders(folder);
	}
});


var FileShare = Class.create({

	files: {},
	getFile: function (file,folder) {
		if (this.files[file.id])
			return this.files[file.id];
		else
			return this.files[file.id] = new FileShareFile(file,folder);
	},
	
	preLoad: function() { 
		if (!this.support.loading && typeof  window.noFlashSupport == 'undefined' ) { 
			window.noFlashSupport = true;
			if (confirm("You need Flash Player version 9.028 or above to upload files. "+
						"(You can download files without it.) \n\nThis is available as a free download. "+
						"Would you like to get it now? (will open in new window)"))
				window.open('http://get.adobe.com/flashplayer/');
			return false;
		} 
	},
	loadFailed: function() { 
		alert("Something went wrong while loading SWFUpload.");
	},
	
	fileQueued: function (file) {
		fileShare.getFile(file,this.customSettings.targetFolder);
	},
	
	fileQueueError: function(file, errorCode, message) {
		try {
			if (errorCode === SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) {
				alert.defer("You have attempted to queue too many files.\n" + (message === 0 ? "You have reached the upload limit." : "You may select " + (message > 1 ? "up to " + message + " files." : "one file.")));
				return;
			}
	
			var f = fileShare.getFile(file, this.customSettings.progressTarget);
			f.setError();
			f.toggleCancel(false);
	
			switch (errorCode) {
			case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
				f.setStatus("File is too big.");
				break;
			case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
				f.setStatus("Cannot upload Zero Byte files.");
				break;
			case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
				f.setStatus("Invalid File Type.");
				alert('Invalid File Type');
				break;
			default:
				if (file !== null) {
					f.setStatus("Unhandled Error");
				}
				break;
			}
		} catch (ex) {
		}
	},
	
	fileDialogComplete: function(numFilesSelected, numFilesQueued) {
		this.startUpload();
	},
	
	uploadStart: function(file) {
		try {
			var ul = this.customSettings.targetFolder,
				f = fileShare.getFile(file,ul);
					
			if (typeof ul.spaceFree != 'undefined' && ul.spaceFree< (file.size/1024/1024)) {
				alert('There is not enough disk space available to upload this file. '+
						'Please delete unused files or get in touch to request more space');
				f.cancel();
			} else if (fileShare.fileShareMaxSize && file.size>fileShare.fileShareMaxSize) {
				alert('That file is too large to upload using this method.');
				f.cancel();
			} else {
				f.setStatus("Uploading...");
				f.toggleCancel(true);
			}
		}
		catch (ex) {}
		
		return true;
	},
	
	uploadProgress: function(file, bytesLoaded, bytesTotal) {
		try {
			var percent = Math.ceil((bytesLoaded / bytesTotal) * 100);
			var f = fileShare.getFile(file, this.customSettings.targetFolder);
			f.setProgress(percent);
			f.setStatus("Uploading...");
		} catch (ex) {
		}
	},
	
	uploadSuccess: function(file, serverData) {
		try {
			var ul = this.customSettings.targetFolder,
				f = fileShare.getFile(file, ul);
			
			f.toggleCancel(false);
			if (serverData.indexOf('fileUpload')===0) {
				try {
					var o = serverData.substr('fileUpload'.length).evalJSON();
					if (o.error) {
						alert('An error occurred - '+o.error);
						var ul = f.el.up('ul');
						if (f.el)
							f.el.remove();
						fileShare.checkForEmptyFolders(ul);
					} else if (o.id) {
						f.el.writeAttribute('filesharefileid',o.id);
						f.el.fileshareFileID = o.id;
												
						if (o.spaceFree && ul.spaceFreeEl) {
							ul.spaceFree = o.spaceFree;
							fileShare.updateFolderSpace(ul);
						}
						f.el.down('.name').update(o.name);
						
					}
				} catch (err) {
					alert("Oh dear - something went wrong. Your file may have uploaded fine though.\n\n Please refresh the page and re-upload the file if it is not listed.");
					if (window.console && window.console.info) console.log(err,serverData);
				}
				f.setComplete();
			} else {
				f.setError();
			}
		} catch (ex) {
		}
	},
	
	uploadError: function(file, errorCode, message) {
		try {
			var f = fileShare.getFile(file, this.customSettings.progressTarget);
			f.setError();
			f.toggleCancel(false);
	
			switch (errorCode) {
			case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
				f.setStatus("Upload Error: " + message);
				break;
			case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
				f.setStatus("Upload Failed.");
				break;
			case SWFUpload.UPLOAD_ERROR.IO_ERROR:
				f.setStatus("Server (IO) Error");
				break;
			case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
				f.setStatus("Security Error");
				break;
			case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
				f.setStatus("Upload limit exceeded.");
				break;
			case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
				f.setStatus("Failed Validation.  Upload skipped.");
				break;
			case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
				// If there aren't any files left (they were all cancelled) disable the cancel button
				if (this.getStats().files_queued === 0) {
					document.getElementById(this.customSettings.cancelButtonId).disabled = true;
				}
				f.setStatus("Cancelled");
				f.setCancelled();
				break;
			case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
				f.setStatus("Stopped");
				break;
			default:
				f.setStatus("Unhandled Error: " + errorCode);
				break;
			}
		} catch (ex) {
		}
	},
	
	uploadComplete: function(file) {
		if (this.getStats().files_queued === 0) {
			//document.getElementById(this.customSettings.cancelButtonId).disabled = true;
		}
	},
	
	// This event comes from the Queue Plugin
	queueComplete: function(numFilesUploaded) {
		//var status = document.getElementById("divStatus");
		//status.innerHTML = numFilesUploaded + " file" + (numFilesUploaded === 1 ? "" : "s") + " uploaded.";
	},
	
	pollForUpdates: function () {
		new Ajax.Request('/_mod/fileshare',{method:'get',parameters:{
								action: 'checkForUpdates',
								folders: Object.toJSON(this.rootFolderNames),
								viewTime: this.viewTime
							},
								onComplete: this.pollForUpdatesCompleteBound
							});
	},
	
	pollForUpdatesComplete: function (r) {
		var foldersToCheck = [];
		
		var data = r.responseText.substr(0,r.responseText.length-10).evalJSON();
		
		if (data.time)
			this.viewTime = data.time;
		
		if (data.updatedDirs) {
			// go through each updated folder
			$H(data.updatedDirs).each(function(o){ 
				var folder = o.key;
				var files = o.value;
				var ul = this.getFolderByPath(folder);
				
				/* Folder doesn't exist - new folder */
				if (!ul) {
					
					var folderName = folder.split('/').last();
					ul = new Element('ul',{
								filesharefolder: folder,
								id:'fileshare-list-'+folder.replace('/','-').replace(/[^a-zA-Z0-9-]/g,'')
							});
					
					var li = new Element('li',{'class':'folder',filesharefilename:folderName})
							.insert(new Element('span',{'class':'name clickable'}).update(folderName))
							.insert(ul);
					
					var parent = this.getFolderByPath(folder.substring(0,folder.lastIndexOf('/')));
					
					this.insertInOrder(parent,li,folderName);
					
					this.initFile(li);
					this.addFileControls(li);
					this.initFolders(ul);
				
				} else {
					// go through all the existing files for that folder
					ul.childElements().each(function(li) {
						if (li.hasClassName('controls') || li.hasClassName('emptyMarker')) return;
						
						if (!this.getName(li)) return;
						
						// look for this <li> in the array of real files
						var found = false;
						for (var i=0;i<files.length;i++) {
							// if found, delete the file from the array
							if (files[i]==this.getFilename(li) || files[i]=='*'+this.getFilename(li)) {
								found = true;
								if (data.updatedFiles && data.updatedFiles[folder+'/'+files[i]])
									this.makeFileNew(li);
								delete files[i];
								break;
							}
						}
						
						
						// if not found, remove the file from display
						if (!found) {
							this.unInitFile(li);
							
							// if this file's folder is open, animate it away
							if (li.up('li').hasClassName('open')) {
								li.addClassName('outgoing');
								new Effect.Fade(li,{afterFinish: function() { li.remove(); }});
							// if it's currently not visible, just remove it
							} else {
								li.remove();
							}
						}
					},this);
				}
				
				foldersToCheck.push(ul);
				
				// the items left in the files array now are new files
								
				// go through each new file for this folder
				files.compact().each(function(newFile) {
					
					var isFolder = (newFile.charAt(0)=='*');
					
					// if the filename has an escaped asterisk, remove the slash
					if (newFile.indexOf('\*')===0)
						newFile = newFile.substring(1);
					
					// file
					if (!isFolder) {
						// look for file data for the new file
						// for modified files, we'll be sent the html and the fileID 
						var fileData = (data.uploadedFiles) ? data.updatedFiles[folder+'/'+newFile] : false;
						
						if (fileData) {
							this.insertInOrder(ul,fileData.html,fileData.name);								
							var li = this.getFileElById(fileData.fileID);
							
						} else {
							var li = new Element('li',{'class':'waiting',filesharefilename:newFile})
									.insert(new Element('span',{'class':'name clickable'}).update(newFile));
							this.insertInOrder(ul,li,newFile);
							this.reloadFile(newFile,folder,li);
						}
					
					// folder
					} else {
						var newUl = new Element('ul',{
											filesharefolder: folder+'/'+newFile,
											id:'fileshare-list-'+folder.replace('/','-').replace(/[^a-zA-Z0-9-]/g,'')
										});
						var li = new Element('li',{'class':'folder',filesharefilename:newFile})
								.insert(new Element('span',{'class':'name clickable'}).update(newFile))
								.insert(newUl);						
						this.insertInOrder(ul,li,newFile);
						this.initFolders(newUl);
					}
					
					this.initFile(li);
					this.addFileControls(li);
					
				},this);
				
			},this);
			
			foldersToCheck = foldersToCheck.uniq();
			if (foldersToCheck.length)
				this.checkForEmptyFolders(foldersToCheck);
		}
	},
	
	insertInOrder: function (ul,li,newName) {		
		if (!newName)
			newName = this.getName(li);
		
		var found = false, controlsEl;
		ul.childElements().find(function(refLi){
			// store reference to the controls <li>, to insert file at bottom of folder
			if (refLi.hasClassName('controls')) {
				controlsEl = refLi;
				return;
			// ignore the li.emptyMarker
			} else if (refLi.hasClassName('emptyMarker')) {
				return;
			// find the file with a name just below (in alphabetical order)
			} else if (this.getName(refLi)>newName) {
				found = true;
				refLi.insert({before: li});
				return true;
			} 
			return false;
		},this);
		
		if (!found) {
			// insert before li.controls
			if (controlsEl)
				controlsEl.insert({before:li});
			// if for some reason there is no li.controls (shouldn't happen) insert at bottom of <ul>
			else
				ul.insert(li);
		}
	},
	
	/* li is optional */
	reloadFile: function (fileName,folder,li) {
		this.ajaxRequest('/_mod/fileshare',{method:'get',parameters:{
					action: 'printFile',
					file: fileName,
					folder: folder
				},
				onComplete: this.reloadFileComplete.bindAsEventListener(this,li)
			});
	},
	
	reloadFileComplete: function (r,el) {
		if (!el)
			el = this.getFileElById(r.headerJSON.fileID);
		
		var showDetails = el.hasClassName('showingDetails');
		this.unInitFile(el);

		el.replace(r.responseText.substr(0,r.responseText.length-10)); // remove 'fileUpload' from end of string
		el = this.getFileElById(r.headerJSON.fileID);
		
		if (showDetails)
			el.addClassName('showingDetails');
			
		this.initFile(el);
		this.addFileControls(el);
		this.makeFileNew(el);
	},
	
	getFileElById: function (id) {
		return $$('li[filesharefileid='+id+']').first();
	},
	getFileElByPath: function (path) {
		
	},
	
	getFolderByPath: function (path) {
		return $$('ul[filesharefolder='+path+']').first();
	},

	getFilename: function (li) {
		if (!li.fileshareFilename)
			li.fileshareFilename = li.readAttribute('filesharefilename');
		return li.fileshareFilename;
	},
	
	getName: function (li) {
		if (!li.name) {
			var newMarker;
			if (newMarker = li.down('.newMarker'))
				newMarker.remove();
			li.name = li.down('.name').innerHTML.strip();
			if (newMarker)
				li.down('.name').insert(newMarker);
		}
		return li.name;
	},
	
	getFileID: function (li) {
		if (!li.fileshareFileID)
			li.fileshareFileID = li.readAttribute('filesharefileid');
		return li.fileshareFileID;
	},
	
	getFolderPath: function (ul) {
		if (!ul.fileshareFolder)
			ul.fileshareFolder = ul.readAttribute('filesharefolder');
		
		return ul.fileshareFolder;
	},
	
	createFolderClick: function (ev) {
		var el = Event.element(ev);
		
		el.update();
		
		var textbox = new Element('input')
							.observe('blur',this.createFolderGo.bind(this))
							.observe('keydown',this.createFolderTextBoxKeypress.bind(this)),
			button = new Element('span',{'class':'createFolder'});
		el.insert(textbox).insert(button);
		
		textbox.focus();
	},
	
	createFolderGo: function (ev) {
		var el = Event.element(ev),
			ul = el.up('ul');
		
		if (ul.creatingFolder) return;

		if (el.value)
			this.createFolder(ul,el.value);
		
		el.up('.newFolder').update('New Folder');
	},
		
	createFolderTextBoxKeypress: function (ev) {
		switch (ev.keyCode) {
			case Event.KEY_ESC:
				Event.element(ev).clear();
			case Event.KEY_RETURN:
				this.createFolderGo(ev);
				break;
		}
	},
	
	createFolder: function (parentFolderUL,newFolderName) {
		var	parent = this.getFolderPath(parentFolderUL),
			li, ul, fullpath;

		parentFolderUL.creatingFolder = true;
		newFolderName = newFolderName.replace(/[^a-zA-Z0-9 -_]/g,'');
		if (!newFolderName) return false;
		
		fullpath = parent+'/'+newFolderName;
		
		ul = new Element('ul',{
					filesharefolder:fullpath,
					id:'fileshare-list-'+fullpath.replace('/','-').replace(/[^a-zA-Z0-9-]/g,'')
				});
		ul.fileshareFolder = fullpath;
			
		li = new Element('li',{'class':'folder waiting'})
					.insert(new Element('span',{'class':'name clickable'}).update(newFolderName))
					.insert(ul);
		
		this.insertInOrder(parentFolderUL,li,newFolderName);
		
		this.checkForEmptyFolders([ul, parentFolderUL]);
		
		this.ajaxRequest('/_mod/fileshare',{method:'post',parameters:{
					action: 'createFolder',
					folder: fullpath,
					_comm_mode: 'ajax'
				},
				onComplete: this.createFolderComplete.bindAsEventListener(this,li)
			});
			
	},
	
	createFolderComplete: function (r,li) {
		if (r.headerJSON.success) {
			li.removeClassName('waiting').addClassName('open');
			var ul = li.up('ul');
			this.initFile(li);
			this.addFileControls(li);
			this.initFolders(li.down('ul'));
			if (li.up('.rootFolder').sortMode)
				this.reloadSortMode(li.up('.rootFolder'));
			ul.creatingFolder = false;
		} else {
			li.remove();
			this.checkForEmptyFolders(li.up('ul'));
		}
	},
	

	renameFolderClick: function (ev) {
		var li = Event.findElement(ev,'li'),
			nameEl = li.down('.name');
		
		this.initFile(li);
		this.getName(li);
		nameEl.update();
		
		var textbox = new Element('input',{value:this.getName(li)})
							.observe('blur',this.renameFolderGo.bind(this))
							.observe('keydown',this.renameFolderTextBoxKeypress.bind(this));
		nameEl.insert(textbox);
		textbox.activate();
	},
	
	renameFolderGo: function (ev) {
		var el = Event.element(ev), // textbox
			li = el.up('li'),
			nameEl = li.down('.name'),
			textboxValue = el.value;
		
		el.remove();
		nameEl.update(this.getName(li)); // set to old name, in case rename fails

		if (textboxValue && textboxValue!=this.getName(li))
			this.renameFolder(li,textboxValue);
	},
		
	renameFolderTextBoxKeypress: function (ev) {
		switch (ev.keyCode) {
			case Event.KEY_ESC:
				Event.element(ev).clear();
			case Event.KEY_RETURN:
				this.renameFolderGo(ev);
				break;
		}
	},
	
	renameFolder: function (li,newFolderName) {
		var	ul = li.down('ul');

		newFolderName = newFolderName.replace(/[^a-z0-9 \-_,.]/gi,'');
		if (!newFolderName) return false;
		
		var path = this.getFolderPath(ul),
			pathBits = path.split('/');
		pathBits.pop();
				
		this.ajaxRequest('/_mod/fileshare',{method:'post',parameters:{
					action: 'moveFolder',
					source: path,
					destination: pathBits.join('/'),
					newFolderName: newFolderName
				},
				onComplete: this.renameFolderComplete.bindAsEventListener(this,li)
			});
			
		li.down('.name').update(newFolderName);
		li.name = newFolderName;
		li.writeAttribute('filesharefilename',newFolderName);
	},
	
	renameFolderComplete: function (r,li) {
		var ul = li.down('ul');
		
		ul.fileshareFolder = r.request.parameters.destination+'/'+r.request.parameters.newFolderName;
		ul.writeAttribute('filesharefolder',ul.fileshareFolder);
		
	},	
	
	
	deleteFile: function (ev) {
		var el = Event.element(ev).up('li');
		
		this.initFile(el);
		
		if (el.hasClassName('waiting')) return false;
		
		var params = {action: 'deleteFile',
					_comm_mode: 'ajax'
					};
		
		if (el.isFolder) {
			var folderID = this.getFolderPath(el.down('ul'));
			params.folder = folderID;
			var name = 'empty folder '+this.getName(el);
		} else {
			var fileID = this.getFileID(el);
			params.fileID = fileID;
			var name = this.getName(el);
		}
		if (!confirm('Are you sure you want to delete '+name+'?')) return false;
		el.setStyle({opacity:0.3}).addClassName('waiting');
		this.ajaxRequest('/_mod/fileshare',{method:'post',parameters:params,
				onComplete: this.deleteFileComplete.bindAsEventListener(this,el)
			});
	},
	
	deleteFileComplete: function (r,li) {
		var o = r.headerJSON,
			ul = li.up('ul');
		if (o.success) {
			li.remove();
		} else {
			li.setStyle({opacity:1}).removeClassName('waiting');
		}
		this.checkForEmptyFolders(ul);
	},
	
	fileNameClick: function (ev) {
		var el = Event.element(ev),
			ul = el.up('ul.rootFolder');
		if (ul.sortMode)
			Event.stop(ev);
	},
	
	toggleDetails: function (ev) {
		var el = Event.element(ev);
		el.up('li').toggleClassName('showingDetails');
		el.update((el.up('li').hasClassName('showingDetails'))?'Hide Details':'Show Details');
	},
	
	toggleFolder: function (ev) {
		var el = Event.element(ev),
			li = el.up('li'),
			ul = li.down('ul');
		li.toggleClassName('open');

		if (!ul.initialised)
			this.initFolder(ul);
			
		if (!ul.swfupload)
			this.initUploadButton(ul);
		
		if (!ul.filesInitialised) {
			ul.childElements().each(function(li){
				this.initFile(li);
				this.addFileControls(li);
			},this);
			ul.fileInitialised = true;
		}
		
		Event.stop(ev);
	},
	
	updateFolderSpace: function (ul) {
		ul.spaceFreeEl.update(ul.spaceFree+'MB space available');
	},
	
	initUploadButton: function (ul) {
		if (ul.swfUploadSettings && !ul.swfupload)
			ul.swfupload = new SWFUpload(ul.swfUploadSettings);
	},
	
	rootFolderNames: [],
	
	/* you can pass an individual <ul>, or pass nothing and it will attempt to init all folders (it won't init a folder twice) */
	initFolders: function (ul) {		
		var uls;
		
		if (Object.isArray(ul))
			uls = ul;
		else if (ul && ul.tagName=='UL')
			uls = [ul]
		else
			uls = this.el.select('ul');
		
		for (var i=0, l=uls.length; i<l; i++)
			this.initFolder(uls[i]);
		
		// init the first layer of <li>s
		this.el.childElements().each(this.initFile,this);
		
		this.checkForEmptyFolders(uls);

	},
	
	initFolder: function (ul) {
		if (ul.initialised) return;
		
		// root folder
		if (ul.up('ul') == this.el) {
			ul.isRootFolder = true;
			ul.addClassName('rootFolder');
			ul.rootFolder = ul;
		}
		
		if (ul.hasAttribute('fileshareSpaceMax')) {
			ul.spaceMax = ul.readAttribute('fileshareSpaceMax');
			if (ul.spaceMax) {
				ul.spaceMaxEl = new Element('div',{'class':'spaceMax'}).update(ul.spaceMax+'MB');
				ul.spaceFree = ul.readAttribute('fileshareSpaceFree');
				if (ul.spaceFree)
					ul.spaceFreeEl = new Element('div',{'class':'spaceFree'}).update(ul.spaceFree+'MB space available');
			}
			var filetypes = ul.readAttribute('fileshareFiletypes');
			if (filetypes)
				ul.allowedFiletypes = filetypes.split(' ');
		} else {
			// find root folder
			var rootFolder = ul;
			while (rootFolder = rootFolder.up('ul'))
				if (rootFolder.isRootFolder) {
					ul.rootFolder = rootFolder;
					break;
				}
		}
		
		if (!window.noFlashSupport) {
			var uploadButton = new Element('span',{'class':'fileshare_upload'}),
				controls = new Element('li',{'class':'controls'});
			
			if (!ul.rootFolder || typeof ul.rootFolder.spaceFree == 'undefined' || ul.rootFolder.spaceFree>0) {
				controls.insert(uploadButton);
				var slashes = this.getFolderPath(ul).match(/\//g);
				if (!slashes || slashes.length<10) { // limits the depth of folders (created via this method)
					var newFolderButton = new Element('span',{'class':'newFolder'})
												.update('New folder')
												.observe('click',this.createFolderClickBound);
					controls.insert(newFolderButton);
				}
				
				if (ul.spaceFreeEl)
					ul.previous('.name').insert({after:ul.spaceFreeEl});

				ul.insert({bottom:controls});			

				var settings = Object.clone(this.settings);
				settings.custom_settings = Object.clone(this.settings.custom_settings);
				settings.post_params = Object.clone(this.settings.post_params);
				settings.post_params.folder = this.getFolderPath(ul);
				
				settings.button_placeholder_id = uploadButton.identify();
				settings.custom_settings.targetFolder = ul;
				settings.swfupload_element_id = ul.id+"-swfupload";	
				settings.degraded_element_id = ul.id+"-swfupload_degrad";
				
				if (ul.rootFolder.allowedFiletypes) {
					settings.file_types = '';
					settings.file_types = ul.rootFolder.allowedFiletypes.map(function(s) { return '*.'+s; }).join(';');
					settings.file_types_description = 'Permitted file types';
				}
				
				ul.swfUploadSettings = settings;
				//settings.button_text = this.getButtonText(ul.up('li').down('.name').innerHTML.strip());
				
				// we'll only init the upload button (swfupload) if the folder is open - to avoid the time penalty of init'ing lots at startup
				if (ul.up('li').hasClassName('open'))
					this.initUploadButton(ul);
				
			}
			
			if (ul.isRootFolder) {
				ul.organiseButton = new Element('span',{'class':'organise'})
											.update('Organise files')
											.observe('click',this.toggleSortMode.bind(this,ul));
				controls.insert(ul.organiseButton);
			}
		}

		// init all the files in this folder
		ul.childElements().each(this.initFileBound);
		
		ul.initialised = true;
	},
	
	getRootFolderClassName: function (el) {
		if (el.up('ul') == this.el)
			el = el.down('ul');
		else if (el.tagName!='UL')
			el = el.up('ul');
		if (!el) return '';
		
		if (!el.rootFolder)
			this.initFolder(el);
		
		return 'rootFolder_'+this.getFolderPath(el.rootFolder).replace(/[^a-zA-Z0-9_\-]+/g,'-');
	},
	
	makeFileNew: function (li,notNew) {
		if (notNew) {
			var el = li.down('.newMarker');
			if (el) 
				el.remove();
			li.removeClassName('new');
		} else {
			if (li.down('.newMarker')) 
				return true;
			li.down('.name').insert(new Element('span',{'class':'newMarker'}).update('NEW'));
			li.addClassName('new');
		}
	},
	
	addFileControls: function (li) {
	
		if (li.hasControls) return;
		
		if (li.hasClassName('folder')) {
			
			li.renameEl = new Element('span',{'class':'rename'})
								.update('Rename')
								.observe('click',this.renameFolderClickBound);
			
			li.down('.name').insert({after:li.renameEl});
		
		//***** FILES ****
		} else { 
			
			var detailsEl = li.down('.details');
			if (!detailsEl) return;
			
			var showDetailsEl = new Element('span',{'class':'showDetails'})
										.update('Show Details')
										.observe('click',this.toggleFileDetailsBound);
			detailsEl.insert({before:showDetailsEl});
			
			// add delete button
			if (li.hasClassName('own')) {
				var deleteEl = new Element('span',{'class':'delete'}).update('Delete').observe('click',this.deleteFileBound);
				detailsEl.insert({before:deleteEl});
			}
		}
		
		li.hasControls = true;
	},
	
	a: 0,
	initFile: function (li) {
		if (li.initialised || li.tagName!='LI') return;	
		
		// get class names once
		var classNames = li.className.split(' '),
			newClassNames = []; // to only have one addClassName call
		
		// ignore controls and empty marker
		if (classNames.indexOf('controls')!==-1 || classNames.indexOf('emptyMarker')!==-1)
			return;
				
		/*****************
		***** FOLDER *****
		*****************/
		if (classNames.indexOf('folder')!==-1) {
		
			li.isFolder = true;
			var ul = li.down('.name').next('ul'),
				controls = ul.childElements().last();

			if (ul.isRootFolder)
				this.rootFolderNames.push(this.getFolderPath(ul));
			
			if (ul || controls) {
				newClassNames.push('clickable');
				li.down('.name').observe('click',this.toggleFolderBound);
			}
			
			this.checkIfFolderIsEmpty(ul);
			
		/*****************
		****** FILE ******
		*****************/
		} else { // file
			
			this.getName(li);
			li.down('.name').observe('click',this.fileNameClickBound);
			newClassNames.push('clickable');
			if (classNames.indexOf('new')!==-1)
				this.makeFileNew(li);
				
		}

		li.initialised = true;
		
		newClassNames.push(this.getRootFolderClassName(li));
		li.addClassName(newClassNames.join(' '));

	},
	
	unInitFile: function (li) {
		if (!li.initialised) return;	
		
		li.writeAttribute('filesharefileid',''); // remove id
		
		if (li.hasClassName('controls') || li.hasClassName('emptyMarker'))
			return;
			
		li.descendants().invoke('stopObserving');
		li.stopObserving();
		
	},
	
	checkForEmptyFolders: function (ul) {
		var uls;
		if (Object.isArray(ul))
			uls = ul;
		else if (ul)
			uls = [ul]
		else
			uls = this.el.select('ul');
		uls.each(this.checkIfFolderIsEmptyBound);
	},
	
	getFileCount: function (ul) {
		var count = 0, li;
		for (var i=0, l=ul.children.length; i<l; i++) {
			li = ul.children[i];
			if (li.tagName!='LI') continue;
			var c = li.className.split(' ');
			
			if ((c.indexOf('controls')===-1) && (c.indexOf('emptyMarker')===-1) && (c.indexOf('outgoing')===-1))
				count++;
		}
		return count;
	},
	
	checkIfFolderIsEmpty: function (ul) {
		// find <li> children, excluding .controls and .emptyMarker
		var liCount = this.getFileCount(ul);
		
		ul.fileCount = liCount;
		if (liCount==0) {
		
			if (!ul.emptyLI)
				ul.emptyLI = new Element('li',{'class':'emptyMarker'})
									.update('Folder is empty');
			ul.insert({top:ul.emptyLI});

			if (!ul.deleteEl && !ul.isRootFolder)
				ul.deleteEl = new Element('span',{'class':'delete'}).update('Delete empty folder')
									.observe('click',this.deleteFileBound);
			ul.up('li').addClassName('empty');
			ul.insert({before:ul.deleteEl});
			
		} else {
			ul.up('li').removeClassName('empty');
			if (ul.emptyLI) {
				ul.emptyLI.remove();
				ul.emptyLI = null;
			}
			if (ul.deleteEl) {
				ul.deleteEl.remove();
				ul.deleteEl = null;
			}
		}
		
	},
	
	// convert rgb(0,0,0) to hex
	rgb2html: function (x, blue, green, red) {
		var decColor = Number(red) + 256*Number(green) + 65536*Number(blue);
		return '#'+decColor.toPaddedString(2,16);
	},
	
	getButtonText: function (folder) {	
		var dest = (folder) ? 'to '+folder : 'here';
		return '<span class="uploadbutton">Upload files '+dest+'</span>';
	},
	
	
	toggleSortMode: function (folder) {
		if (folder.sortMode) {
			locks.release('fileshare.organise',this.getFolderPath(folder));
			this.sortModeOff(folder);
		} else {
			var organiseButton = folder.down('.organise');
			organiseButton.removeClassName('locked').addClassName('pending');
			locks.request('fileshare.organise',this.getFolderPath(folder),{
										callback: this.sortLockCallback.bind(this,folder)
									});
		}
	},
	
	sortLockCallback: function (folder,lock) {
		var organiseButton = folder.down('.organise');

		organiseButton.removeClassName('pending');
		if (lock.state=='obtained') {
			organiseButton.removeClassName('locked');
			this.sortModeOn(folder);
		} else {
			organiseButton
				.addClassName('locked')
				.update('Sorry, '+lock.lockInfo.userName+' is currently organising these files. ')
				.insert(new Element('input',{'type':'button',value:'Try again'}));
		}
	},
	
	foldersInSortMode: 0,
	
	/* Folder is a rootFolder <ul> */
	sortModeOn: function (folder) {
		
		if (!this.fileMoveDropBound)
			this.fileMoveDropBound = this.fileMoveDrop.bind(this);
		if (!this.fileMoveHoverBound)
			this.fileMoveHoverBound = this.fileMoveHover.bind(this);
		if (!this.fileDragStartBound)
			this.fileDragStartBound = this.fileDragStart.bind(this);
		if (!this.fileDragEndBound)
			this.fileDragEndBound = this.fileDragEnd.bind(this);
		
		if (!folder.sortMode) {
			if (this.foldersInSortMode==0)
				Draggables.addObserver({
									element: 'fileshareSortMode',
									onStart: this.fileDragStartBound,
									onEnd: this.fileDragEndBound
								});
			this.foldersInSortMode++;
	
			this.initFolders(folder.select('ul'));
			folder.organiseButton.addClassName('active').update('Finish organising files');
			folder.addClassName('sortMode');
			folder.sortMode = true;
		}
		
		this.makeDroppable(folder);
		
		folder.select('li').reverse().each(function(li){
			if (li.isFolder) {
				this.makeDroppable(li);
			} 
			
			if (!li.hasClassName('controls') && !li.hasClassName('emptyMarker')) {
				this.makeDraggable(li);
			}
		},this);
		
	},
	
	/* Folder is a rootFolder <ul> 
	
		Set reloading to true if you are immediately going to call sortModeOn();
	*/
	sortModeOff: function (folder,reloading) {

		Droppables.remove(folder);
		
		folder.select('li').reverse().each(function(li){
			if (li.isFolder) {
				Droppables.remove(li);
				if (li.drag)
					li.drag.destroy();
			} else if (li.hasClassName('controls') || li.hasClassName('emptyMarker')) {
				return;
			} else {
				if (li.drag)
					li.drag.destroy();
			}
		},this);
		
		if (!reloading) {
			this.foldersInSortMode--;
			if (this.foldersInSortMode==0)
				Draggables.removeObserver('fileshareSortMode');
			folder.organiseButton.removeClassName('active').update('Organise files');
			folder.removeClassName('sortMode');
			folder.sortMode = false;
		}
	},
	
	reloadSortMode: function (folder) {
		this.sortModeOff(folder,true);
		this.sortModeOn();
	},
	
	makeDroppable: function (el) {
		Droppables.add(el,{
			accept: this.getRootFolderClassName(el),
			overlap: 'vertical',
			hoverclass: 'hovertarget',
			onHover: this.fileMoveHoverBound,
			onDrop: this.fileMoveDropBound
		});
	},
	
	makeDraggable: function (fileLi) {
		fileLi.drag = 
			new Draggable(fileLi,{
				revert: 'failure',
				scroll: window
			});
	},
	
	fileDragStart: function (eventName,draggable) {
		draggable.element.addClassName('dragging');
		this.dropMarker = draggable.element.clone(true).addClassName('dropMarker').setOpacity(0.5);
		this.dropMarker.name = draggable.element.name;
	},

	fileDragEnd: function (eventName,draggable) {
		draggable.element.removeClassName('dragging');
		if (this.dropMarker.parentNode)
			this.dropMarker.remove();
		delete this.dropMarker;
	},
	
	fileMoveHover: function (draggable,folder) {
		if (folder.tagName=='LI')
			folder = folder.down('ul');
		if (folder==draggable.parentNode) {
			if (this.dropMarker.parentNode)
				this.dropMarker.remove();
		} else if (folder.fileCount==0) {
			folder.insert(this.dropMarker);
		} else {
			this.insertInOrder(folder,this.dropMarker);
		}
	},
	
	fileMoveDrop: function (file, folder, ev) {
		var sourceFolder = file.parentNode.up('li');
		
		file.setStyle({	position: 'static',top:0,left:0});

		if (sourceFolder!=folder) {
			if (folder.tagName=='LI')
				folder = folder.down('ul');
			
			var files = folder.childElements();
			
			this.insertInOrder(folder,file);
			
			file.addClassName('waiting');
			
			// turn sorting off until this request has been completed
			this.pendingMove = folder.rootFolder;
			this.sortModeOff(folder.rootFolder,true);
			
			// send command to server to move file
			if (file.isFolder) { // folder
				var ul = file.down('ul');
				this.ajaxRequest('/_mod/fileshare',{method:'post',parameters:{
									action: 'moveFolder',
									source: this.getFolderPath(ul),
									destination: this.getFolderPath(folder)
								},
								onComplete: this.fileMoved.bind(this,file)
							});
				
				ul.fileshareFolder = this.getFolderPath(folder) + '/' + this.getName(file);
				ul.writeAttribute('filesharefolder',this.getFolderPath(ul));
				
			} else { // file
				this.ajaxRequest('/_mod/fileshare',{method:'post',parameters:{
									action: 'moveFile',
									fileID: this.getFileID(file),
									folder: this.getFolderPath(folder)
								},
								onComplete: this.fileMoved.bind(this,file)
							});
			}
			
			// ensure controls are the last <li>
			folder.insert({bottom:folder.childElements().find(function(li){ return li.hasClassName('controls'); })});

			this.checkForEmptyFolders([file.parentNode,folder]);
		}
		file.drag.destroy();
		this.makeDraggable(file);
	},
	
	fileMoved: function (li, r,o) {
		// turn sort mode back on
		this.sortModeOn(this.pendingMove);
		
		li.removeClassName('waiting');
		this.pendingMove = undefined;
	},
	
	ajaxRequest: function (url,args) {
		if (window.ajaxManager)
			ajaxManager.request(url,args);
		else
			new Ajax.Request(url,args);
	},
	
	continueInit: function () {
		this.toggleFileDetailsBound = this.toggleDetails.bind(this);
		this.toggleFolderBound = this.toggleFolder.bind(this);
		this.deleteFileBound = this.deleteFile.bind(this);
		this.createFolderClickBound = this.createFolderClick.bind(this);
		this.fileNameClickBound = this.fileNameClick.bind(this);
		this.pollForUpdatesCompleteBound = this.pollForUpdatesComplete.bind(this);
		this.addFileControlsBound = this.addFileControls.bind(this);
		this.initFolderBound = this.initFolder.bind(this);
		this.checkIfFolderIsEmptyBound = this.checkIfFolderIsEmpty.bind(this);
		this.initFileBound = this.initFile.bind(this);
		this.renameFolderClickBound = this.renameFolderClick.bind(this);

		// default button text style - can be overridden in CSS - see below
		var button_text_style = ".uploadbutton { \
										font-family: Arial; \
										color: #000; \
										font-size: 12px; \
										text-transform: uppercase; \
										leading: 21; \
										margin-left:7;  \
										margin-top:0; \
									}";
		
		/* Attempts to read the CSS, to extract a declaration for styling .uploadbutton, which can then be passed to Flash */
		
		var foundCSS = false;
		for (var i2=0; i2<document.styleSheets.length; i2++) {
			var cssRules = (Object.isUndefined(document.styleSheets[i2].cssRules)) ? document.styleSheets[i2].rules : document.styleSheets[i2].cssRules; // cross-browser access
			for (var i=0; i<cssRules.length; i++) {
				if (cssRules[i].selectorText == '.fileshare .uploadbutton') {
					var s = (!cssRules[i].cssText) 
								? '.uploadbutton { '+cssRules[i].style.cssText.toLowerCase()+'; }'  // IE
								: cssRules[i].cssText;												// other browsers
					if (!s) continue;
					s = s.replace(/\s*\.fileshare\s+/,'') // remove .fileshare prefix
						 .replace(/\srgb\s*\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/gi,this.rgb2html) // convert colours to hex
						 .replace(/\bline-height\b/gi,'leading') // change 'line-height' to 'leading;
						 .replace(/([0-9]+\s*)px/gi,'$1'); // remove 'px' units
					button_text_style = s;
					
					foundCSS = true;
					break;
				}
			}
			if (foundCSS) break;
		}
					
		this.settings = { 
			upload_url : "/_mod/fileshare", 
			flash_url : "/lib/SWFUpload/swfupload_fp9.swf", 
			flash9_url : "/lib/SWFUpload/swfupload_fp9.swf", 
			post_params: {"action" : "uploadFile"},
			use_query_string: true,
			file_size_limit : "100 MB",
			file_types : "*.*",
			file_types_description : "All Files",
			file_upload_limit : 100,
			file_queue_limit : 0,
			custom_settings : {}, // needs to be defined as an object, even if it has no properties
	
			// Button settings
			//button_image_url: "images/TestImageNoText_65x29.png",
			button_width: 75,
			button_height: 19,
			button_text_top_padding: 2,
			button_text: '<span class="uploadbutton">Upload files here</span>',
			button_text_style: button_text_style,
			button_cursor : SWFUpload.CURSOR.HAND, 
			button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT, 
			
			// The event handler functions are defined in handlers.js
			swfupload_preload_handler : this.preLoad,
			swfupload_load_failed_handler : this.loadFailed,
			file_queued_handler : this.fileQueued,
			file_queue_error_handler : this.fileQueueError,
			file_dialog_complete_handler : this.fileDialogComplete,
			upload_start_handler : this.uploadStart, 
			upload_progress_handler : this.uploadProgress,
			upload_error_handler : this.uploadError,
			upload_success_handler : this.uploadSuccess,
			upload_complete_handler : this.uploadComplete,
			queue_complete_handler : this.queueComplete	// Queue plugin event
		};
		
		
		// we need to send the PHP Session ID via GET to authenticate (because Flash doesn't send browser cookies)
		var cookie;
		if (cookie = document.cookie.match(/\bPHPSESSID=(.*?)(;|$)/))
			this.settings.post_params.PHPSESSID = cookie[1];
				
		if (window.fileShareMaxSize)
			this.fileShareMaxSize = window.fileShareMaxSize;
			
		this.viewTime = window.fileShareViewTime;
		
		// tell the server to pass data back in an AJAXy way
		this.settings.post_params._comm_mode = 'ajax';
		
		// just init the root folders
		this.initFolders($$('.fileshare>li>ul'));

		quickMessage.hide.bind(quickMessage).defer('loadingFiles');

		if (!this.updatesPollTimer)
			this.updatesPollTimer = new PeriodicalExecuter(this.pollForUpdates.bind(this),10);

		//console.timeEnd('fileshare');
	},
	
	initialize: function () {
	
		if (!window.ajaxManager)
			window.ajaxManager = new AjaxManager();
		if (!window.quickMessage)
			window.quickMessage = new QuickMessage();
		
		this.el = $$('.fileshare').first();
		
		quickMessage.message('Loading files...',{
									persist: true, 
									button: false, 
									ref: 'loadingFiles',
									top: this.el.viewportOffset().top+15
								});
		
		this.continueInit.bind(this).defer();
	}

});

if (typeof window.fileShare == 'undefined') {
	if (document.loaded)
		window.fileShare = new FileShare();
	else
		document.observe('dom:loaded',function() { window.fileShare = new FileShare(); });
}
