/**

* @version		$Id: manager.js 84 2009-06-11 19:04:04Z happynoodleboy $

* @package      JCE

* @copyright    Copyright (C) 2005 - 2009 Ryan Demmer. All rights reserved.

* @author		Ryan Demmer

* @license      GNU/GPL

* JCE is free software. This version may have been modified pursuant

* to the GNU General Public License, and as distributed it includes or

* is derivative of works licensed under the GNU General Public License or

* other free or open source software licenses.

*/



/**

Class: Manager

	Base manager class for creating a JCE Manager object.



Arguments:

	options - optional, an object containing options.



Options:

	interface 		- various interface identifiers.

	filter 			- file extension filter list

	tree 			- use folder tree (requires tree.js)

	onDeleteFiles 	- Delete files callback function.

	onDeleteFolder 	- Delete folder callback function.

	onRename 		- Fodler / file rename callback function.

	onNewFolder 	- New folder callback function.

	onListComplete 	- File / folder list load complete callback function.

	onFileClick 	- File click callback function.

	onFileDetails	- File details callback function.



Example:

	var imgmanager = new Manager('imgmanager', src, args, {params: {'key': 'value'}});

*/

var Manager = Plugin.extend({

	_actions 		: [],

	_buttons 		: {

		'folder'	: [],

		'file'		: []

	},

	// Selected files array

	_selectedItems 	: [],

	_selectedIndex 	: [],

	_activeItem		: 0,	

	// Returned items array

	_returnedItems 	: [],

	// 'Clipboard'

	_pastefiles 	: '',

	_pasteaction	: '',	

	// Options

	moreOptions : function(){

		return {

			// Various dialog containers

			interface: {

				list: 		'dir-list',

				tree: 		'tree-body',

				info: 		'info-text',

				comments:	'info-comment',

				nav: 		'info-nav',

				status: 	'message-status',

				message:	'message-info',

				buttons: 	'buttons',

				actions: 	'actions',

				refresh: 	'refresh',

				search: 	'search',

				sortExt:	'sort-ext',

				sortName: 	'sort-name'

			},

			actions:		null,

			buttons:		null,

			tree:			true,

			upload:			{

				size: 1024,

				types: {},

				conflict: 'all',

				limit: false,

				onLoad: Class.empty

			},

			onInit:			Class.empty,

			onFileInsert:	Class.empty,

			onFileDelete: 	Class.empty,

			onFolderDelete: function(node){

				if(this.treeLoaded){

					// Remove tree node

					this.tree.removeNode(node);

				}

			}.bind(this),

			onFileRename: 	Class.empty,

			onFolderRename:	function(node, name){

				if(this.treeLoaded){

					// Rename tree node

					this.tree.renameNode(node, name);

				}	

			}.bind(this),

			onFolderNew: 	Class.empty,

			onLoadList: 	function(o){

				o.folders.each(function(e){

					$('folder-list').adopt(

						new Element('li').addClass('folder').addClass(e.classes).setProperties({'id': encodeURI(e.id), 'title': e.name}).addEvent('click', function(event){

							this.setSelectedItems(event, false);

						}.bind(this)).adopt(

							new Element('a').setProperty('href', 'javascript:;').setHTML(e.name).addEvent('click', function(){

								this.changeDir(e.id);

							}.bind(this))

						)

					)

				}.bind(this));				

				$('file-list').empty();

				if(o.files.length){

					o.files.each(function(e){

						$('file-list').adopt(

							new Element('li', {

								'title'	: e.name,

								events: {

									click: function(event){

										this.setSelectedItems(event, true)	

									}.bind(this),

									dblclick: function(){

										return false;

									}.bind(this)

								}

							}).addClass('file').addClass(string.getExt(e.name)).addClass(e.classes).adopt(

								new Element('a').setProperty('href', 'javascript:void(0);').setHTML(e.name).addEvent('click', function(){

									this.fireEvent('onFileClick', [e.name]);

								}.bind(this))

							)

						)

					}.bind(this));

				}else{

					$('file-list').adopt(

						new Element('li').addClass('nofile').setHTML(tinyMCEPopup.getLang('dlg.no_files', 'No files'))

					)	

				}

			},

			onListComplete: Class.empty,

			onFileClick:	Class.empty,

			onFileDetails:	Class.empty

		};

	},

	/**

	 * Initialise the class

	 * @param {String} plugin 	The plugin name.

	 * @param {String} src 		The returned src if any.

	 * @param {Object} vars 	Optional variables object.

	 * @param {Object} vars 	Optional options object.

	 */

	initialize : function(plugin, src, vars, options){		

		// Set options

		this.setOptions(this.moreOptions(), options);

		this.parent(plugin, this.options);

		// Preload image files

		new Asset.images([this.getImage('libraries.icons.gif'), this.getImage('libraries.ext.gif')]);

		// Load theme language file

		this.loadLanguage();

		/* Setup default values */

		this._vars = vars;

		/* Create Actions and Button */

		this.addActions(this.options.actions);

		this.addButtons(this.options.buttons);

		

		/* Build file and folder lists */

		$(this.options.interface.list).adopt([

			new Element('ul').addClass('item-list').setProperty('id', 'folder-list'),

			new Element('ul').addClass('item-list').setProperty('id', 'file-list')

		])

		/* Info navigation buttons */

		$(this.options.interface.nav + '-left').addEvent('click', function(){

			this._activeItem--;

			if(this._activeItem < 0){

				this._activeItem = 0;	

			}

			this.showFileDetails();

		}.bind(this));

		$(this.options.interface.nav + '-right').addEvent('click', function(){

			this._activeItem++;

			var n = this._selectedItems.length;

			if(this._activeItem > n-1){

				this._activeItem = n-1;	

			}

			this.showFileDetails();

		}.bind(this));



		/* Sortables */

		new ListSorter(this.options.interface.sortExt, 'ext', ['file-list']);

		new ListSorter(this.options.interface.sortName, 'name', ['folder-list', 'file-list']);

		/* Searchables*/

		new Searchables(this.options.interface.search, this.options.interface.list, 'file-list', {

			onFind : function(el){

				if(el.length){

					this.selectNoItems();

					this.selectItems(el, true);	

				}else{				 	

					this.selectNoItems();

				}

			}.bind(this)

		});

		/* Setup refresh button */

		$(this.options.interface.refresh).addEvent('click', function(){

			this.refreshList();

		}.bind(this))

		

		this.setupDir(src);

		this.fireEvent('onInit');

	},

	/**

	 * Set up the base directory

	 * @param {String} src The base url

	 */

	setupDir : function(src){

		var p = '/', f = '', base = this.getParam('base'), n = base.length;

		if(src){

			if(src.substring(0, n) == base){

				p = string.dirname(src).replace(base, '', 'g') || '/';

				f = string.basename(src) || '';

			}

		}else{

			p = Cookie.get('jce_' + this.getPlugin() + '_dir') || '/';

			f = '';

		}

		this._dir = string.path('/', p);

		this.addReturnedItem(f);

		

		if(this.treeLoaded){

			// Initialize tree view

			this.initTree();

		}else{

			// Load folder / file list

			this.getList();	

		}

	},

	/**

	 * Check if the Tree option is set and the Tree Class is loaded

	 * return Boolean.

	 */

	treeLoaded : function(){

		return this.options.tree && typeof Tree != 'undefined';

	},

	/**

	 * Initialize the Tree

	 */

	initTree : function(){

		/* Initialise tree */

		this.setStatus(tinyMCEPopup.getLang('dlg.message_tree', 'Building tree list...'), true);

		this.tree = new Tree(this.options.interface.tree, {

			onInit : function(fn){

				this.xhr('getTree', this._dir, function(o){

					// Set default tree

					$(this.options.interface.tree).setHTML(o);

					// Init callback

					fn.apply();

					// Load folder / file list

					this.getList(this._dir);

				}.bind(this));

			}.bind(this),

			// When a node is clicked

			onNodeClick : function(el, node){

				this.changeDir(node.id);

			}.bind(this),

			// When a node is toggled and loaded

			onNodeLoad : function(node){

				this.tree.toggleLoader(node);

				this.xhr('getTreeItem', encodeURI(node.id), function(o){

					if(o){

						if(!o.error){

							var ul = $E('ul', node);

							if(ul){

								ul.remove();	

							}

							this.tree.createNode(o.folders, node.id);

							this.tree.toggleNodeState(node, true);

						}else{

							alert(o.error);	

						}

					}

					this.tree.toggleLoader(node);

				}.bind(this));

			}.bind(this)

		});

	},

	/**

	 * Reset the Manager

	 */

	resetManager : function(){

		// Clear selects

		this.selectNoItems();

		// Clear returns

		this._returnedItems	= [];

		// Close any dialogs

		this._dialog.each(function(dialog){

			if(typeof dialog.close() != 'undefined'){

				dialog.close();	

			}

		});

	},

	/**

	 * Clear the Paste action

	 */

	clearPaste : function(){

		// Clear paste 

		this._pasteaction = '';

		this._pastefiles  = '';

		this.hideButton(this.getButton('file', 'paste').element);

	},

	/**

	 * Set a status message

	 * @param {String} message

	 * @param {String} loading

	 */

	setStatus : function(message, loading){

		$(this.options.interface.status).className = loading ? 'load' : '';

		$(this.options.interface.status).setHTML('<span>' + message + '</span>');

	},

	/**

	 * Set a message

	 * @param {String} message

	 * @param {String} classname

	 */

	setMessage : function(message, classname){

		$(this.options.interface.message).className = classname || 'info';

		$(this.options.interface.message).setHTML('<span>' + message + '</span>');

	},

	/**

	 * Sets a loading message

	 */

	setLoader : function(){

		this.setStatus(tinyMCEPopup.getLang('dlg.message_load', 'Loading...'), true);

	},

	/**

	 * Reset the message display

	 */

	resetMessage : function(){

		this.setMessage(tinyMCEPopup.getLang('dlg.file_select', 'Click on a file name to select for insert.'), 'info');

	},

	/**

	 * Reset the status display

	 */

	resetStatus : function(){

		this.setStatus(tinyMCEPopup.getLang('dlg.current_dir', 'Current directory is: ') + this._dir + ' ( ' + this._foldercount + ' ' + tinyMCEPopup.getLang('dossiers', 'dossiers') + ', ' + this._filecount + ' ' + tinyMCEPopup.getLang('fichiers', 'fichiers') + ' )');

	},

	/**

	 * Get the parent directory

	 * @return {String} s The parent/previous directory.

	*/

	getPreviousDir : function(){

		if(this._dir.length < 2){

    		return this._dir;

		}

		var dirs = this._dir.split('/');

		var s = '/';

		for(var i = 0; i < dirs.length-1; i++){

			s = string.path(s, dirs[i]);

		}

		return s;

	},

	/**

	 * Add an item to the returnedItems array

	 * @return {String} file The item name.

	*/

	addReturnedItem : function(file){

		this._returnedItems.include(file);

	},

	/**

	* Setup the returned file after upload

	* @param {String} file The returning file name.

	*/

	returnFile : function(file){

		this.addReturnedItem(string.basename(file));

		this.changeDir(string.dirname(file));

	},

	/**

	 * Change Directory

	 * @param {String} dir

	 */

	changeDir: function(dir){

		this.resetManager();

		this.setDir(dir);

		this.getList();

	},

	/**

	* Retrieve a list of files and folders

	*/

	getList : function(){

		this.action = $E('form').action + '&upload-dir=' + this._dir;

		Cookie.set("jce_" + this.getPlugin() + '_dir', this._dir, 1);

		this.setLoader();

		this.hideButtons('folder');

		this.hideButtons('file');

		this.xhr('getItems', [this._dir, this._vars], this.loadList);

	},

	/**

	 * Refresh the file list

	 */

	refreshList : function(){

		this.resetManager();

		this.getList();

	},

	/**

	 * Set the current directory

	 * @param {String} dir

	 */

	setDir: function(dir){

		this._dir = decodeURIComponent(dir);

	},

	/**

	 * Get the current directory

	 */

	getDir: function(){

		return this._dir;

	},

	/**

	  Determine whether current directory is root

	 */

	isRoot : function(){

		return this._dir == '' || this._dir == '/';

	},

	/**

	* Load the file/folder list into the container div

	* @param {Object} The folder/file JSON object

	*/

	loadList: function(o){	

		$('folder-list').empty();

		this._foldercount 	= o.folders.length;

		this._filecount 	= o.files.length;

		if(!this.isRoot()){

			$('folder-list').adopt(

				new Element('li').addClass('folder').addClass('folder-up').setProperties({'title': 'Up'}).adopt(

					new Element('a').setProperty('href', 'javascript:;').setHTML('...')

				).addEvent('click', function(){

					this.changeDir(this.getPreviousDir());

				}.bind(this))

			)

		}

		if(this.options.tree){

			this.tree.createNode(o.folders, this._dir);

		}

		// Alternate loadList function

		this.fireEvent('onLoadList', [o]);

		/*Hover fix for IE*/

		if(window.ie6){

			$ES('li', '.item-list').each(function(e){

				e.addEvent('mouseover', function(){

					e.addClass('hover');

				});

				e.addEvent('mouseout', function(){

					e.removeClass('hover');

				});

			});

		}

		/* Browser specific selection settings */

		$ES('li', this.options.interface.list).each(function(e){

			if(window.gecko){

				e.setStyle('-moz-user-select', 'none');

			}else{

				e.setProperty('unselectable', 'on');

			}

		});

		if(this._returnedItems.length){

			this.selectItemsByName(this._returnedItems);

			this._returnedItems = [];

		}

		if(this._pastefiles !== ''){

			this.showButton(this.getButton('file', 'paste').element);

		}

		this.resetStatus();

		this.resetMessage();

		this.fireEvent('onListComplete');

	},

	/**

	* Execute a command

	* @param {String} The command name

	* @param {String} The command type

	*/

	execCommand : function(name, type){		

		var dir 	= this._dir;

		var list	= this.serializeSelectedItems();

		switch(name){

			case 'help':

				this.openHelp('manager');

				break;

			case 'insert':

				this.fireEvent('onFileInsert', [list]);

				break;

			case 'view':

				var url 		= string.path(string.path(this.getSite(), this.getParam('base')), string.path(this._dir, this._selectedItems[0].title));	

				var name 		= string.basename(this._selectedItems[0].title);

				var viewable 	= this.getParam('viewable') || 'jpeg,jpg,gif,png';

				if(viewable.split(',').contains(string.getExt(name))){

					if (/\.(js|asp|php|vb|vbs|exe|ocx|dll)/i.test(name)) {

						return false;

					}

					if(/\.(jpeg|jpg|gif|png|avi|wmv|wm|asf|asx|wmx|wvx|mov|qt|mpg|mp3|mp4|mpeg|swf|flv|xml|dcr|rm|ra|ram|divx)/i.test(name)){

						new mediaPreview(name, url, {

							width: 400,

							height: 400

						});

					}else{

						new iframeDialog(name, url, {

							width: 500,

							frameHeight: 500,

							modal: true

						});

					}

				}

				break;

			case 'upload':

				this._dialog['upload'] = new uploadDialog({

					extended : this.options.upload,

					onOpen : function(){

						// Set hidden dir value to current dir

						$('upload-dir').value = this._dir;

						// Set overwrite options

						var o = {

							'overwrite' : new Option(tinyMCEPopup.getLang('dlg.overwrite', 'Overwrite file'), 0),

							'unique'	: new Option(tinyMCEPopup.getLang('dlg.unique', 'Create unique name'), 1)

						}

						var s = $('upload-overwrite');

						var x = this.options.upload.conflict.split('|');

						

						x.each(function(e){

							s.options[s.options.length] = o[e];				

						});

						o = null;

						// Initialize uploader

						this.uploader = new Uploader('upload-queue', {															 

							url		:	$E('form').getProperty('action'),

							field	:	$E('input[name^=Filedata]'),

							size	:	this.options.upload.size,

							limit	: 	this.options.upload.limit,

							filter	:	this.options.upload.types,

							fileComplete : function(file, name){

								// Set uploaded files

								this.addReturnedItem(string.safe(name));

							}.bind(this),

							uploadComplete : function(){

								$('upload-submit').disabled = false;

								// Reset action

								this.action = $E('form').action;

								// Refresh file list

								this.getList();

							}.bind(this)

						});

						this.options.upload.onLoad.delay(10);

					}.bind(this),

					onUpload : function(){

						if(this.uploader.uploading) return false;

						this.uploader.upload();

						return false;

					}.bind(this)

				});

				break;

			case 'folder_new':

				this._dialog['folder_new'] = new Prompt(tinyMCEPopup.getLang('dlg.folder_new', 'Nouveau dossier'), {

					text: tinyMCEPopup.getLang('dlg.name', 'Nom'),

					onConfirm: function(){

						var folder = $('prompt').value;

						if(folder){

							this.setLoader();

							this.xhr('folderNew', [dir, string.safe(folder)], function(o){		  															  

								if(!o.error){

									this.fireEvent('onFolderNew');

									this.refreshList();

									this._dialog['folder_new'].close();

								}else{

									this.raiseError(o.error);

								}

							})

						}

					}.bind(this)

				});

				break;

			// Cut / Copy operation

			case 'copy':

			case 'cut':

				this._pasteaction 	= name;

				this._pastefiles 	= list;

				this.showButton(this.getButton('file', 'paste').element, true);

				break;

			// Paste the file

			case 'paste':

				var fn = (this._pasteaction == 'copy') ? 'fileCopy' : 'fileMove';

				this.setLoader();

				this.xhr(fn, [this._pastefiles, dir], function(o){		  															  

					if(!o.error){

						this.fireEvent('onPaste');

						this.refreshList();

						this.clearPaste();

					}else{

						this.raiseError(o.error);

					}

				}.bind(this))

				break;

			// Delete a file or folder

			case 'delete':

				var msg = tinyMCEPopup.getLang('dlg.delete_folder_alert', 'Delete Folder?');

				var fn 	= 'folderDelete';

				if(type == 'file'){

					msg = tinyMCEPopup.getLang('dlg.delete_file_alert', 'Delete Files(s)?');

					fn 	= 'fileDelete';	

				}

				this._dialog['confirm'] = new Confirm(msg, function(state){

						if(state){

							this.setLoader();

							this.xhr(fn, list, function(o){		  															  

								if(!o.error){

									if(fn == 'folderDelete'){

										this.fireEvent('onFolderDelete', list);

									}else{

										this.fireEvent('onFileDelete');	

									}

									this.refreshList();

								}else{

									this.raiseError(o.error);

								}

							})

						}

					}.bind(this)

				);

				break;

			// Rename a file or folder

			case 'rename':

				var msg = tinyMCEPopup.getLang('dlg.rename_folder', 'Rename Folder');

				var fn 	= 'folderRename';

				var v	= string.basename(list);

				if(type == 'file'){

					msg = tinyMCEPopup.getLang('dlg.rename_file', 'Rename File');

					fn 	= 'fileRename';

					v	= string.basename(string.stripExt(list));

				}

				this._dialog['rename'] = new Prompt(msg, {

					text: tinyMCEPopup.getLang('name', 'Nom'),

					value : v,

					onConfirm : function(){

						var name = string.safe($('prompt').value);

						this._dialog['confirm'] = new Confirm(tinyMCEPopup.getLang('dlg.rename_alert', 'Renommer le fichier/dossier rendra invalides les liens y conduisant. Continuer?'), function(state){

							if(state){

								this.setLoader();

								this.xhr(fn, [list, name], function(o){		  															  

									if(!o.error){

										this.resetManager();

										if(fn == 'folderRename'){

											this.addReturnedItem(string.path(this._dir, name));

											this.fireEvent('onFolderRename', [list, string.path(this._dir, name)]);

										}else{

											this.addReturnedItem(string.path(this._dir, name + '.' + string.getExt(list)));

											this.fireEvent('onFileRename');	

										}

										this._dialog['rename'].close();

										this.getList();

									}else{

										this.raiseError(o.error);

									}

								});

							}

						}.bind(this));

					}.bind(this)

				});

				break;

		}

	},

	/**

	 * Show an error dialog

	 * @param {String} error

	 */

	raiseError : function(error){

		this._dialog['alert'] = new Alert(error, {

			onClose: function(){

				this.refreshList();	

			}.bind(this)

		});

	},

	/**

	 * Add an array of actions

	 * @param {Object} actions

	 */

	addActions : function(actions){

		$each(actions, function(e){

			this.addAction(e);

		}.bind(this));

	},

	/**

	 * Add an action to the Manager

	 * @param {Object} options

	 */

	addAction : function(options){

		var name 	= options.name;

		var action	= eval(options.action) || this.execCommand;

		var atn = new Element('div', {'id': name, 'title': options.title}).addClass('action').addClass(name).setStyles({

			'cursor': 'pointer'

		});

		if(options.icon){

			btn.setStyle('background-image', string.path(this.getPluginUrl(), options.icon));	

		}

		if(options.name){

			atn.addEvent('click', function(){

				action.pass([name], this)();														   

			}.bind(this))	

		}

		if(window.ie){

			atn.addEvent('mouseover', function(){

				atn.addClass('hover');   

			}).addEvent('mouseout', function(){

				atn.removeClass('hover');   

			});

		}

		this._actions[name] = atn;

		$(this.options.interface.actions).adopt(atn);

	},

	/**

	 * Get an action by name

	 * @param {String} name

	 */

	getAction : function(name){

		return this._actions[name];

	},

	/**

	 * Add an array of buttons to the Manager

	 * @param {Object} btns

	 */

	addButtons : function(btns){

		$each(btns.folder, function(e){

			this.addButton(e, 'folder');

		}.bind(this));

		$each(btns.file, function(e){

			this.addButton(e, 'file');

		}.bind(this));

	},

	/**

	 * Add a button to the Manager

	 * @param {Object} options

	 * @param {String} type

	 */

	addButton : function(options, type){

		var action	= options.action || this.execCommand;

		var btn = new Element('div').setProperty('title', options.title).addClass('button').addClass(options.name).addClass('hide').setStyles({

			'cursor': 'pointer'

		});

		if(options.icon){

			btn.setStyle('background-image', string.path(this.getPluginUrl(), options.icon));	

		}

		if(options.name){

			var n = options.name;

			btn.addEvent('click', function(){

				if(this._selectedItems){

					eval(action).pass([n, type], this)();

				}

			}.bind(this))	

		}

		if(window.ie){

			btn.addEvent('mouseover', function(){

				btn.addClass('hover');   

			}).addEvent('mouseout', function(){

				btn.removeClass('hover');   

			});

		}

		this._buttons[type].include({'name': options.name, 'element': btn, 'trigger': options.trigger, 'multiple': options.multiple});

		$(this.options.interface.buttons).adopt(btn);

	},

	/**

	 * Hide all buttons

	 */

	hideAllButtons : function(){

		$$('div.button').each(function(e){

			this.hideButton(e);   

		}.bind(this));

	},

	/**

	 * Hide buttons by type

	 * @param {String} type The button type

	 */

	hideButtons : function(type){

		this._buttons[type].each(function(e){

			this.hideButton(e.element);

		}.bind(this));

	},

	/**

	 * Hide a button

	 * @param {String} button The button to hide

	 */

	hideButton : function(button){

		if(button){

			if(button.hasClass('show')){

				button.removeClass('show');

				button.addClass('hide');

			}

		}

	},

	/**

	 * Show all buttons by type

	 * @param {String} type The button type to show

	 */

	showButtons : function(type){

		this.hideAllButtons();

		this._buttons[type].each(function(e){

			if(!e.trigger){

				this.showButton(e.element, e.multiple);

			}

		}.bind(this));

	},

	/**

	 * Show a button

	 * @param {String} button The button to show

	 * @param {Boolean} multiple Whether a button is a multiple selection action

	 */

	showButton : function(button, multiple){

		if(button){

			if(button.hasClass('hide')){

				button.removeClass('hide');

				button.addClass('show');

			}

			if(this._selectedItems.length > 1 && !multiple){

				this.hideButton(button);

			}

		}

	},

	/**

	 * Get a button

	 * @param {String} type The button type

	 * @param {String} name The button name

	 */

	getButton : function(type, name){

		var btn;

		this._buttons[type].each(function(el){

			if(el.name == name){

				btn = el;

			}

		});

		return btn;

	},

	/**

	 * Determine whether an item is selected

	 * @param {Object} el The list item

	 */

	isSelectedItem : function(el){

		// Quick check

		if($type(el) == 'element'){

			return el.hasClass('selected') && this._selectedItems.contains(el);

		}

		// Check on element or element title/name

		this._selectedItems.each(function(e){

			return e.title == el;

		});

	},

	/**

	 * Deselect all list items

	 */

	selectNoItems : function(){

		this._selectedItems.each(function(el){

			if($(el)){

				el.removeClass('selected');

			}

		});

		this._selectedItems = [];

		this._activeItem 	= 0;

		$(this.options.interface.info).empty();

		$(this.options.interface.comments).empty();

		// Shortcut for nav

		var nav = this.options.interface.nav;

		[nav + '-left', nav + '-right', nav + '-text'].each(function(el){

			$(el).setStyle('visibility', 'hidden');											  

		});

		this.hideAllButtons();

	},

	/**

	 * Select an array of items

	 * @param {Array} items The array of items to select

	 * @param {Boolean} show Show item properties

	 */	

	selectItems : function(items, show){

		this._selectedItems.merge(items).each(function(el){

			if($(el)){

				el.addClass('selected');

			}

		}.bind(this));	

		if(show){

			this.showSelectedItems();

		}

	},

	/**

	 * Remove items from a selection

	 * @param {Array} el Array of elements to remove

	 * @param {Boolean} show Show remaining item properties

	 */

	removeSelectedItems : function(el, show){

		el.each(function(o){

			if(o){

				o.removeClass('selected');

				this._selectedItems.remove(o);	

			}

		}, this);

		if(show){

			this.showSelectedItems();

		}

	},

	/**

	 * Return selected items by key or all selected items

	 * @param {String} key Item key

	 */

	getSelectedItems : function(key){

		return this._selectedItems[key] || this._selectedItems;

	},

	/**

	* Process a selection click

	* @param {String} e The click event.

	* @param {Boolean} multiple Allow multiple selections.

	*/

	setSelectedItems : function(e, multiple){

		e = new Event(e);

		// the selected element

		var el = e.target;

			

		// If not li element, must be a so get parent li

		if(el.getTag() != 'li') el = el.getParent();

        

        // Prevent double clicking

		if (this.isSelectedItem(el) && this._selectedItems.length == 1) {

			e.stop();

			return false;

		}

		

		// if folder selected, remove file selections & select folder

		if(el.hasClass('folder')){

			if(this.isSelectedItem(el)){

				//return this.removeSelectedItems([el], true);	

			}

			this.selectNoItems();

			this.selectItems([el], e.target.getTag() == 'a' ? false : true);

		// selection hit is a file

		}else{

			// Get file items

			var items = $ES('li', 'file-list');

			// Single click

			if(!e.control && !e.shift || !multiple){

				this._selectedIndex = items.indexOf(el);

				if(this.isSelectedItem(el) && this._selectedItems.length == 1){

					//return this.removeSelectedItems([el], true);

				}			

				// deselect all

				this.selectNoItems();

				if(this._selectedItems.length == 0){

					if(this.isSelectedItem(el)){

						this.removeSelectedItems([el], true);

					}else{

						this.selectItems([el], true);

					}

				}

			// ctrl & shift

			}else if(multiple && (e.control || e.shift)){

				// Remove selected folder

				if(this._selectedItems.length == 1 && this._selectedItems[0].hasClass('folder')){

					this.removeSelectedItems([this._selectedItems[0]], true);

				}			

				// ctrl

				if(e.control){

					this._selectedIndex = items.indexOf(el);

					if(this.isSelectedItem(el)){

						this.removeSelectedItems([el], true);

					}else{

						this.selectItems([el], true);

					}

				}

				// shift

				if(e.shift){

					if(this._selectedItems.length){

						// selected item index

						var si 		= this._selectedIndex;

						// click item index

						var ci		= items.indexOf(el);				

						var selection 	= [];

						

						// Clear selection

						this.selectNoItems();

						// Clicked item further up list than selected item

						if(ci > si){

							for(var i=ci; i>=si; i--){

								selection.include(items[i]);

							}

						}else{

							// Clicked item further down list than selected item

							for(var i=si; i>=ci; i--){

								selection.include(items[i]);

							}	

						}

						this.selectItems(selection, true);

					}else{

						this.selectItems([el], true);	

					}

				}

			}

		}

	},

	/**

	* Show the selected items' details

	*/

	showSelectedItems : function(){

		var n = this._selectedItems.length;

		if(!n){

			this.resetManager();

		}else{

			if(this._selectedItems[0].hasClass('folder')){

				this.showFolderDetails();				

			}else{

				this._activeItem = n-1;

				this.showFileDetails();

			}

		}

	},

	/**

	* Select an item (file) by name

	* @param {String} name The file name.

	*/

	selectItemsByName : function(names, type){

		var items = [];

		if(!type) type = 'file';

		if($type(names) == 'string'){

			names = [names];

		}

		names.each(function(e){

			if(e){

				items.merge($ES('li[title='+ e +']', type + '-list'));

			}

		}.bind(this));

		if(items.length){

			// Set item index for last selection

			this._selectedIndex = $ES('li', type + '-list').indexOf(items[items.length-1]);

			// Scroll to first item in list

			new Fx.Scroll(this.options.interface.list, {

				wait: false,

				duration: 1000

			}).toElement(items[0]);

		}

		// Select items and display properties

		this.selectItems(items, true);

	},

	/**

	* Serialize the current item selection, add current dir to path

	*/

	serializeSelectedItems : function(){

		var s = [];

		this._selectedItems.each(function(e){

			s.include(string.path(this._dir, e.title));

		}.bind(this));

		return s.join(',');

	},

	/**

	 * Show a file number

	 */

	showFileNumber : function(){

		var n = this._selectedItems.length;

		// Shortcut for nav

		var nav = this.options.interface.nav;

		if(this._activeItem){

			$(nav + '-left').setStyle('visibility', 'visible');	

		}else{

			$(nav + '-left').setStyle('visibility', 'hidden');	

		}

		if(this._activeItem + 1 < n){

			$(nav + '-right').setStyle('visibility', 'visible');	

		}else{

			$(nav + '-right').setStyle('visibility', 'hidden');	

		}

		$(nav + '-text').setStyle('visibility', 'visible').setHTML(this._activeItem + 1 + ' of ' + n);

	},

	/**

	 * Show a files properties / details

	 */

	showFileDetails : function(){		

		var n = this._selectedItems.length;

		// Shortcut for nav

		var nav = this.options.interface.nav;

		if(n < 2){

			[nav + '-left', nav + '-right', nav + '-text'].each(function(el){

				$(el).setStyle('visibility', 'hidden');											  

			});

		}

		this.getFileDetails();

		this.fireEvent('onFileDetails');

		

		if(n > 1){

			this.showFileNumber(n);

		}

		this.showButtons('file');

	},

	/**

	 * Show a folder's details / properties

	 */

	showFolderDetails : function(){

		this.getFolderDetails();		

		this.showButtons('folder');

	},

	/**

	 * Get a folder's details properties

	 */

	getFolderDetails : function(){

		var title 	= string.basename(this._selectedItems[0].title);

		

		var info = new Element('dl').adopt([

			new Element('dt').setHTML(title),					

			new Element('dd').setHTML(tinyMCEPopup.getLang('dlg.folder', 'Folder')),

			new Element('dd').setProperty('id', 'loader')

		]);		

		$(this.options.interface.info).empty().adopt(info);

		

		var comments = [];

		if($(this._selectedItems[0]).hasClass('notwritable')){

			comments.include(

				new Element('dd').addClass('comments').addClass('folder').addClass('notwritable').adopt(

					new Element('span').addClass('hastip').setProperty('title', tinyMCEPopup.getLang('dlg.notwritable_desc', 'Unwritable')).setHTML(tinyMCEPopup.getLang('dlg.notwritable', 'Unwritable'))

				)

			)

		}

		if($(this._selectedItems[0]).hasClass('notsafe')){

			comments.include(

				new Element('dd').addClass('comments').addClass('folder').addClass('notsafe').adopt(

					new Element('span').addClass('hastip').setProperty('title', tinyMCEPopup.getLang('dlg.bad_name_desc', 'Bad file or folder name')).setHTML(tinyMCEPopup.getLang('dlg.bad_name', 'Bad file or folder name'))

				)

			)

		}

		this.xhr('getFolderDetails', string.path(this._dir, this._selectedItems[this._activeItem].title), function(o){

			if($('loader')){

				$('loader').remove();

			}

			var props = [];

			$each(o, function(v, k){

				props.include(

					new Element('dd').setHTML(tinyMCEPopup.getLang('dlg.' + k, k) + ': ' + v)			  

				)					   

			}.bind(this));

			info.adopt(props);

			if(comments.length){

				$(this.options.interface.comments).empty().adopt(

					new Element('dl').adopt(

						new Element('dt').setHTML(tinyMCEPopup.getLang('dlg.comments', 'Comments'))					

					).adopt(comments)			

				)

				this.addToolTip($ES('span.hastip', $$('dd.comments')));

			}

			this.fireEvent('onFolderDetails', [o]);

		}.bind(this));

	},

	/**

	 * Get a files details / properties

	 */

	getFileDetails : function(){

		var file 		= this._selectedItems[this._activeItem];

		var title 		= $(file).title;

	

		// Info list

		var info = new Element('dl').adopt([

			new Element('dt').setHTML(string.stripExt(title)), 

			new Element('dd').setHTML(string.getExt(title).toUpperCase() + ' ' + tinyMCEPopup.getLang('dlg.file', 'File')), 

			new Element('dd').setProperty('id', 'info-properties'), 

			new Element('dd').setProperty('id', 'info-preview'), 

			new Element('dd').setProperty('id', 'loader')

		]);

		$(this.options.interface.info).empty().adopt(info);

		

		this.xhr('getFileDetails', string.path(this._dir, this._selectedItems[this._activeItem].title), function(o){

			if ($('loader')) {

				$('loader').remove();

			}

			$each(o, function(v, k){

				// If a button trigger or triggers

				if (o.trigger) {

					o.trigger.each(function(t){

						if (t !== '') {

							var b = this.getButton('file', t);

							if (b) {

								this.showButton(b.element, b.multiple);

							}

						}

					}.bind(this));

				}

				if (!/(trigger|preview)/i.test(k)) {

					$('info-properties').adopt(new Element('dd').setProperty('id', 'info-' + k.toLowerCase()).setHTML(tinyMCEPopup.getLang('dlg.' + k, k) + ': ' + v));

				}

			}.bind(this));

			// Preview

			if (o.preview) {

				var w = o.preview.width || 100;

				var h = o.preview.height || 100;

				$('info-preview').adopt(new Element('dl').adopt([new Element('dt').setHTML(tinyMCEPopup.getLang('dlg.preview', 'Preview') + ': '), new Element('dd').setStyle('height', h).adopt(new Asset.image(o.preview.src, {

					width: w,

					height: h,

					title: 'Preview',

					onload: function(){

						// Show image and remove loader

						this.setStyle('display', '').getParent().removeClass('loader');

					},

					// Remove loader and show error icon

					onerror: function(){

						this.setStyle('display', 'none').getParent().removeClass('loader').addClass('preview-error');

					}

				// Hide preview image while loading

				}).setStyle('display', 'none')).addClass('loader')]))	

			}

			// Comments

			var comments = [];

			if (/not(writable|safe)/i.test($(file).className)) {

				comments.include(new Element('dt').setHTML(tinyMCEPopup.getLang('dlg.comments', 'Comments')));

				// not writable

				if ($(file).hasClass('notwritable')) {

					comments.include(new Element('dd').addClass('comments').addClass('file').addClass('notwritable').adopt(new Element('span').addClass('hastip').setProperty('title', tinyMCEPopup.getLang('dlg.notwritable_desc', 'Unwritable')).setHTML(tinyMCEPopup.getLang('dlg.notwritable', 'Unwritable'))))

				}

				// not safe

				if ($(file).hasClass('notsafe')) {

					comments.include(new Element('dd').addClass('comments').addClass('file').addClass('notsafe').adopt(new Element('span').addClass('hastip').setProperty('title', tinyMCEPopup.getLang('dlg.bad_name_desc', 'Bad file or folder name')).setHTML(tinyMCEPopup.getLang('dlg.bad_name', 'Bad file or folder name'))))

				}

			}

			if (comments.length) {

				$(this.options.interface.comments).empty().adopt(new Element('dl').adopt(comments))

				// Add tooltip

				this.addToolTip($ES('span.hastip', $$('dd.comments')));

			}

			// Fire event

			this.fireEvent('onFileDetails', [o]);

		}.bind(this));

	}

});

// Implement Manager class

Manager.implement(new Events, new Options);