Ext.namespace('Zarafa.plugins.files');

/**
 * @class Zarafa.plugins.files.ABOUT
 * @extends String
 *
 * The copyright string holding the copyright notice for the Kopano files Plugin.
 */
Zarafa.plugins.files.ABOUT = ""
+ "<p>Copyright (C) 2005-2016  Zarafa B.V. &lt;info@zarafa.com&gt; and its licensors</p>"
+ "<p>Copyright (C) 2016 Kopano &lt;info@kopano.com&gt; and its licensors</p>"

+ "<p>This program is free software: you can redistribute it and/or modify "
+ "it under the terms of the GNU Affero General Public License as "
+ "published by the Free Software Foundation, either version 3 of the "
+ "License, or (at your option) any later version.</p>"

+ "<p>This program 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.</p>"

+ "<p>You should have received a copy of the GNU Affero General Public License "
+ "along with this program.  If not, see <a href=\"http://www.gnu.org/licenses/\" target=\"_blank\">http://www.gnu.org/licenses/</a>.</p>";
Ext.namespace('Zarafa.plugins.files');

/**
 * @class Zarafa.plugins.files.FilesContext
 * @extends Zarafa.core.Context
 *
 * This class will add a new context to the webapp. The new context
 * offers a filebrowser for the Files backend.
 */
Zarafa.plugins.files.FilesContext = Ext.extend(Zarafa.core.Context, {

	/**
	 * When searching, this property marks the {@link Zarafa.core.Context#getCurrentView view}
	 * which was used before {@link #onSearchStart searching started}.
	 *
	 * @property
	 * @type Mixed
	 * @private
	 */
	oldView: undefined,

	/**
	 * When searching, this property marks the {@link Zarafa.core.Context#getCurrentViewMode viewmode}
	 * which was used before {@link #onSearchStart searching started}.
	 *
	 * @property
	 * @type Mixed
	 * @private
	 */
	oldViewMode: undefined,

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			current_view     : Zarafa.plugins.files.data.Views.LIST,
			current_view_mode: Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW
		});

		this.registerModules();

		this.registerInsertionPoint('main.maintabbar.left', this.createMainTab, this);

		this.registerInsertionPoint('main.maintoolbar.new.item', this.createNewFilesButton, this);

		this.registerInsertionPoint('main.toolbar.actions.last', this.createMainToolbarButtons, this);

		this.registerInsertionPoint('navigation.center', this.createNavigatorTreePanel, this);

		Zarafa.plugins.files.FilesContext.superclass.constructor.call(this, config);

		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.attachdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.createfolderdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.fileinfopanel');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.sharedialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.uploadstatusdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.treecontextmenu');
	},

	/**
	 * Adds a new tab item to the top tab bar of the WebApp.
	 *
	 * @returns {Object} The button for the top tab bar.
	 */
	createMainTab: function () {
		return {
			text         : this.getDisplayName(),
			tabOrderIndex: 7,
			context      : this.getName()
		};
	},

	/**
	 * This method returns the context model for the files context.
	 * If the model was not yet initialized, it will create a new model.
	 *
	 * @return {Zarafa.plugins.files.FilesContextModel} The files context model.
	 */
	getModel: function () {
		if (!Ext.isDefined(this.model)) {
			this.model = new Zarafa.plugins.files.FilesContextModel();
		}
		return this.model;
	},

	/**
	 * Bid for the given {@link Zarafa.hierarchy.data.MAPIFolderRecord folder}
	 * This will bid on any folder of container class 'IPF.Files'.
	 *
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder for which the context is bidding.
	 * @return {Number} 1 when the contexts supports the folder, -1 otherwise.
	 */
	bid: function (folder) {

		if (folder.isContainerClass('IPF.Files', true)) {
			return 1;
		}

		return -1;
	},

	/**
	 * Bid for the type of shared component and the given record.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @returns {Number}
	 */
	bidSharedComponent: function (type, record) {
		var bid = -1;

		if (Ext.isArray(record)) {
			record = record[0];
		}

		switch (type) {
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.attachdialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.fileinfopanel']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog']:
				bid = 1;
				break;
			case Zarafa.core.data.SharedComponentType['common.create']:
			case Zarafa.core.data.SharedComponentType['common.view']:
			case Zarafa.core.data.SharedComponentType['common.preview']:
				if (record instanceof Zarafa.core.data.IPMRecord && record.isMessageClass('IPM.Files', true)) {
					bid = 1;
				}
				break;
			case Zarafa.core.data.SharedComponentType['common.contextmenu']:
				if (record instanceof Zarafa.core.data.IPMRecord && record.isMessageClass('IPM.Files', true)) {
					bid = 1;
				}
				break;
			default :
				break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent: function (type, record) {
		var component;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.fileinfopanel']:
				component = Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.create']:
				component = Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog']:
				component = Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog']:
				component = Zarafa.plugins.files.ui.dialogs.ShareContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog']:
				component = Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.view']:
			case Zarafa.core.data.SharedComponentType['common.preview']:
				component = Zarafa.plugins.files.ui.FilesRecordViewPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.contextmenu']:
				component = Zarafa.plugins.files.ui.FilesMainContextMenu;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu']:
				component = Zarafa.plugins.files.ui.FilesTreeContextMenu;
				break;
			default :
				break;
		}
		return component;
	},

	/**
	 * Creates the files tree that is shown when the user selects the files context from the
	 * button panel. It shows a tree of available accoutns and folders.
	 *
	 * @return {Object}
	 */
	createNavigatorTreePanel: function () {
		return Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.getNavigatorTreePanelContainer(this);
	},

	/**
	 * This method creates the {@link Zarafa.plugins.files.ui.FilesMainPanel main content panel}
	 * which will contain the file browser.
	 *
	 * @returns {Object}
	 */
	createContentPanel: function () {
		return {
			xtype  : 'filesplugin.filesmainpanel',
			context: this
		};
	},

	/**
	 * Create "New File" {@link Ext.menu.MenuItem item} for the "New item"
	 * {@link Ext.menu.Menu menu} in the {@link Zarafa.core.ui.MainToolbar toolbar}.
	 * This button should be shown in all {@link Zarafa.core.Context contexts} and
	 * is used to upload a new file.
	 *
	 * @returns {Object}
	 */
	createNewFilesButton: function () {
		return {
			xtype       : 'menuitem',
			text        : dgettext('plugin_files', 'Upload file'),
			tooltip     : dgettext('plugin_files', 'Upload one or more files'),
			plugins     : 'zarafa.menuitemtooltipplugin',
			iconCls     : 'icon_files_category',
			newMenuIndex: 6,
			context     : this.getName(),
			handler     : function () {
				Zarafa.plugins.files.data.Actions.openCreateFilesContent(this.getModel());
			},
			scope       : this
		};
	},

	/**
	 * Returns the buttons for the dropdown list of the VIEW-button in the main toolbar. It will use the
	 * main.maintoolbar.view.files insertion point to allow other plugins to add their items at the end.
	 *
	 * @return {Array} An array of components.
	 */
	getMainToolbarViewButtons: function () {
		var items = container.populateInsertionPoint('main.maintoolbar.view.files', this) || [];

		var defaultItems = [{
			overflowText : dgettext('plugin_files', 'No preview'),
			iconCls      : 'icon_previewpanel_off',
			text         : dgettext('plugin_files', 'No preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.NO_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}, {
			overflowText : dgettext('plugin_files', 'Right preview'),
			iconCls      : 'icon_previewpanel_right',
			text         : dgettext('plugin_files', 'Right preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}, {
			overflowText : dgettext('plugin_files', 'Bottom preview'),
			iconCls      : 'icon_previewpanel_bottom',
			text         : dgettext('plugin_files', 'Bottom preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}];

		defaultItems.push();

		return defaultItems.concat(items);
	},

	/**
	 * Adds buttons to the main toolbar like the view switcher button.
	 *
	 * @return {Array}
	 */
	createMainToolbarButtons: function () {
		return [{
			xtype    : 'splitbutton',
			ref      : '../../../filesSwitchViewButton',
			tooltip  : dgettext('plugin_files', 'Switch view'),
			scale    : 'large',
			iconCls  : 'icon_viewswitch',
			handler  : function () {
				this.showMenu();
			},
			menu : {
				xtype : 'menu',
				items: [{
					text        : dgettext('plugin_files', 'List'),
					overflowText: dgettext('plugin_files', 'List'),
					tooltip     : dgettext('plugin_files', 'List'),
					iconCls     : 'icon_contact_list',
					valueView   : Zarafa.plugins.files.data.Views.LIST,
					handler     : this.onSwitchView,
					scope       : this
				}, {
					text        : dgettext('plugin_files', 'Icons'),
					overflowText: dgettext('plugin_files', 'Icons'),
					tooltip     : dgettext('plugin_files', 'Icons'),
					iconCls     : 'icon_note_icon_view',
					valueView   : Zarafa.plugins.files.data.Views.ICON,
					handler     : this.onSwitchView,
					scope       : this
				}]
			},
			listeners: {
				afterrender: this.onAfterRenderMainToolbarButtons,
				menuhide : function(splitBtn, viewMenu){
					viewMenu.find().forEach(function(item){
						var hasClass = item.getEl().hasClass('x-menu-item-selected');
						if(hasClass) {
							item.getEl().removeClass('x-menu-item-selected');
						}
					}, this);
				},
				menushow : function (splitBtn, viewMenu) {
					var menuItem = viewMenu.find('valueView', this.getCurrentView())[0];
					if (Ext.isDefined(menuItem)) {
						menuItem.addClass('x-menu-item-selected');
					}
				},
				scope : this
			}
		}]
	},

	/**
	 * Registers to the {@link Zarafa.core.Container#contextswitch contextswitch} event on the
	 * {@link Zarafa.core.Container container} so the visiblity of the button can be toggled
	 * whenever the context is switched. We do this after the button is rendered.
	 *
	 * @param {Ext.Button} btn The button
	 */
	onAfterRenderMainToolbarButtons: function (btn) {
		btn.mon(container, 'contextswitch', function (parameters, oldContext, newContext) {
			this.setVisiblityMainToolbarButton(btn, newContext);
		}, this);

		btn.mon(this, 'viewchange', function (context, newView, oldView) {
			this.setVisiblityMainToolbarButton(btn, context);
		}, this);

		this.setVisiblityMainToolbarButton(btn);
	},

	/**
	 * Determines whether the passed button has to be shown or not based on what
	 * {@link Zarafa.core.Context Context} is active. If no Context is supplied as an argument it
	 * will get that from the {@link Zarafa.core.Container container}.
	 *
	 * @param {Ext.Button} btn The button.
	 * @param {Zarafa.core.Context} activeContext (Optionial} The active Context.
	 */
	setVisiblityMainToolbarButton: function (btn, activeContext) {
		activeContext = activeContext || container.getCurrentContext();
		if (activeContext === this) {
			btn.show();
		} else {
			btn.hide();
		}
	},

	/**
	 * Event handler which is fired when one of the view buttons has been pressed.
	 *
	 * @param {Ext.Button} button The button which was pressed
	 */
	onSwitchView: function (button) {
		var viewMode = this.getCurrentViewMode();
		this.switchView(button.valueView, viewMode);
	},

	/**
	 * Event handler which is fired when one of the View buttons
	 * has been pressed. This will call {@link #setView setView}
	 * to update the view.
	 *
	 * @param {Ext.Button} button The button which was pressed
	 */
	onContextSelectView: function (button) {
		this.getModel().setDataMode(button.valueDataMode);

		var view = button.valueView;
		var viewMode = button.valueViewMode;

		if (!Ext.isDefined(button.valueView)) {
			view = this.getCurrentView();
		}
		if (!Ext.isDefined(button.valueViewMode)) {
			viewMode = this.getCurrentViewMode();
		}

		this.switchView(view, viewMode);

		this.getModel().setPreviewRecord(undefined, true);
	},

	/**
	 * This method registers the Files module names to the main WebApp.
	 */
	registerModules: function () {
		Zarafa.core.ModuleNames['IPM.FILES'] = {
			list: 'filesbrowsermodule',
			item: 'filesbrowsermodule'
		}
	}
});

/**
 * This code gets executed after the WebApp has loaded.
 * It hooks the context to the WebApp.
 */
Zarafa.onReady(function () {

	if (container.getSettingsModel().get('zarafa/v1/plugins/files/enable') === true) {
		container.registerContext(new Zarafa.core.ContextMetaData({
			name             : 'filescontext',
			displayName      : dgettext('plugin_files', 'Files'),
			allowUserVisible : false,
			pluginConstructor: Zarafa.plugins.files.FilesContext
		}));
	}
});
Ext.namespace('Zarafa.plugins.files.context');

/**
 * @class Zarafa.plugins.files.FilesContextModel
 * @extends Zarafa.core.ContextModel
 *
 * This class will instantiate a new {@link Zarafa.plugins.files.data.FilesRecordStore files store} object.
 */
Zarafa.plugins.files.FilesContextModel = Ext.extend(Zarafa.core.ContextModel, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object.
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.store)) {
			config.store = Zarafa.plugins.files.data.singleton.FilesRecordStoreManager.getStore(); // get the default store
		}

		Zarafa.plugins.files.FilesContextModel.superclass.constructor.call(this, config);
	},

	/**
	 * Create a new {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 *
	 * @param {String} parentid id of the parent folder
	 * @return {Zarafa.plugins.files.data.FilesRecord} The new {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 */
	createRecord: function (parentid) {
		parentid = parentid || "/";

		var record = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Files', {
			store_entryid : "files",
			parent_entryid: parentid
		});

		return record;
	},

	/**
	 * Update the current preview {@link Zarafa.core.data.IPMRecord}
	 * This will fire the event {@link #previewrecordchange}.
	 *
	 * @param {mixed} record The record which is set as preview or false to refresh the old record
	 * @param {Boolean} refresh (optinal) true to just refresh the old record
	 */
	setPreviewRecord: function (record, refresh) {
		if (container.getCurrentContext().getName() === "filescontext") {
			var previewPanel = Zarafa.plugins.files.data.ComponentBox.getPreviewPanel();
			var panelConstructor;

			if (refresh && this.previewRecord) {

				panelConstructor = container.getSharedComponent(Zarafa.core.data.SharedComponentType['common.preview'], this.previewRecord);

				previewPanel.removeAll();
				if (Ext.isDefined(panelConstructor)) {
					previewPanel.add(new panelConstructor());
					previewPanel.doLayout();
					previewPanel.fileinfo.update(this.previewRecord);
				}

			} else if (this.previewRecord !== record) {
				this.previewRecord = record;

				if (Ext.isDefined(record)) {
					panelConstructor = container.getSharedComponent(Zarafa.core.data.SharedComponentType['common.preview'], record);

					if (Ext.isDefined(panelConstructor) && previewPanel.fileinfo instanceof panelConstructor) {
						previewPanel.fileinfo.update(record);
					} else {

						previewPanel.removeAll();
						if (panelConstructor) {
							previewPanel.add(new panelConstructor());
							previewPanel.doLayout();
							previewPanel.fileinfo.update(record);
						}
					}
				} else {
					previewPanel.removeAll();
				}
			}
		}
	},

	/**
	 * Returns a list of currently selected folders.
	 * @return {Zarafa.hierarchy.data.MAPIFolderRecord|Array} selected folders as an array of
	 * {@link Zarafa.hierarchy.data.MAPIFolderRecord MAPIFolder} objects.
	 */
	getFolders: function () {
		Zarafa.plugins.files.data.FilesMAPIFolderRecord = Ext.extend(Zarafa.hierarchy.data.MAPIFolderRecord, {
			getFullyQualifiedDisplayName: function () {
				return this.getDisplayName();
			},
			getParentFolder             : function () {
				return false;
			},
			set                         : function (name, data) {
				if (Ext.isDefined(this.data.name)) {
					this.data.name = data;
				}
			}
		});
		var pseudoFolder = new Zarafa.plugins.files.data.FilesMAPIFolderRecord({
			icon_index     : Zarafa.core.mapi.IconIndex.getValue('files'),
			display_name   : dgettext('plugin_files', 'Files'),
			entryid        : this.store.getPath(),
			parent_entryid : this.store.getPath(),
			store_entryid  : "files",
			folder_pathname: Zarafa.plugins.files.data.Utils.File.stripAccountId(this.store.getPath()),
			content_unread : 0,
			content_count  : 0
		}, this.store.getPath());

		return [pseudoFolder];
	},

	/**
	 * Event handler for the {@link Zarafa.hierarcy.data.HierarchyStore#load load} event.
	 * This will set {@link #onContextSwitch onContextSwitch listener} on
	 * {@link Zarafa.core.Container container} contextswitch event.
	 * @param {Zarafa.core.hierarchyStore} store that holds hierarchy data.
	 */
	onHierarchyLoad : function (hierarchyStore)
	{
		// only continue when hierarchyStore has data
		if(hierarchyStore.getCount() === 0) {
			return;
		}
		container.on('contextswitch', this.onContextSwitch, this);
		this.onContextSwitch(null, null, container.getCurrentContext()); // do a initial check after render
	},

	/**
	 * Fires on context switch from container. Updates folder tree visibility
	 * @param {Object} parameters contains folder details
	 * @param {Context} oldContext previously selected context
	 * @param {Context} newContext selected context
	 *
	 * @private
	 */
	onContextSwitch : function (parameters, oldContext, newContext)
	{
		var navPanel = container.getNavigationBar();

		if(newContext instanceof Zarafa.plugins.files.FilesContext) {
			this.oldShowFolderList = navPanel.showFolderList; // store old value
			navPanel.setShowFolderList(false); // disable "view all folders"
		} else if(Ext.isDefined(this.oldShowFolderList)) {
			navPanel.setShowFolderList(this.oldShowFolderList); // reset old value
		}
	}

});
Ext.namespace('Zarafa.plugins.files');

/**
 * @class Zarafa.plugins.files.FilesPlugin
 * @extends Zarafa.core.Plugin
 *
 * This class integrates the Files plugin to the core WebApp.
 * It allows users to set up and manage their Files accounts.
 */
Zarafa.plugins.files.FilesPlugin = Ext.extend(Zarafa.core.Plugin, {

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		this.registerModules();

		Zarafa.plugins.files.FilesPlugin.superclass.constructor.call(this, config);
	},

	/**
	 * This method is called by the parent and will initialize all insertion points
	 * and shared components.
	 */
	initPlugin: function () {
		Zarafa.plugins.files.FilesPlugin.superclass.initPlugin.apply(this, arguments);

		this.registerInsertionPoint('context.settings.categories', this.createSettingCategories, this);

		this.registerInsertionPoint('main.attachment.method', this.createAttachmentDownloadInsertionPoint, this);

		this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentUploadInsertionPoint, this);

		this.registerInsertionPoint('context.mail.contextmenu.actions', this.createEmailUploadInsertionPoint, this);

		Zarafa.core.mapi.IconIndex.addProperty("files");

		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.accountedit');
		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.featurequotainfo');
		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.featureversioninfo');
		Zarafa.core.data.SharedComponentType.addProperty('common.dialog.attachments.files');
		Zarafa.core.data.SharedComponentType.addProperty('common.dialog.attachments.savetofiles');

		Zarafa.plugins.files.data.singleton.BackendController.init();
		Zarafa.plugins.files.data.singleton.AccountStore.init();
		Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.setUpListeners();
	},

	/**
	 * Create the files {@link Zarafa.settings.SettingsMainCategory Settings Category}
	 * to the {@link Zarafa.settings.SettingsContext}. This will create new
	 * {@link Zarafa.settings.ui.SettingsCategoryTab tabs} for the
	 * {@link Zarafa.plugins.files.settings.SettingsFilesCategory Files Plugin}.
	 * @return {Object} configuration object for the categories to register
	 */
	createSettingCategories: function () {
		return {
			xtype: 'filesplugin.settingsmaincategory'
		}
	},

	/**
	 * This method hooks to the attachments chooser button and allows users to add files from
	 * the Files plugin to their emails.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createAttachmentDownloadInsertionPoint: function (include, btn) {
		return {
			text   : dgettext('plugin_files', 'Add from Files'),
			handler: this.showFilesDownloadAttachmentDialog.createDelegate(this, [btn]),
			scope  : this,
			iconCls: 'icon_files_category'
		};
	},

	/**
	 * Function will be called before {@link Zarafa.common.attachment.ui.AttachmentContextMenu AttachmentContextMenu} is shown
	 * so we can decide which item should be disabled.
	 * @param {Zarafa.core.ui.menu.ConditionalItem} item context menu item
	 * @param {Zarafa.core.data.IPMAttachmentRecord} record attachment record on which context menu is shown
	 */
	onAttachmentUploadBeforeShow : function(item, record) {
		// embedded messages can not be downloaded to files
		item.setDisabled(record.isEmbeddedMessage());
		// unsaved attachments can not be added to files without depending on Webapp internals (AttachmentState)
		item.setDisabled(record.isTmpFile());
	},

	/**
	 * This method hooks to the attachment context menu and allows users to store files from
	 * their emails to the  Files plugin.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createAttachmentUploadInsertionPoint: function (include, btn) {
		return {
			text   : dgettext('plugin_files', 'Add to Files'),
			handler: this.showFilesUploadAttachmentDialog.createDelegate(this, [btn]),
			scope  : this,
			iconCls: 'icon_files_category',
			beforeShow : this.onAttachmentUploadBeforeShow
		};
	},

	/**
	 * This method hooks to the email context menu and allows users to store emails from
	 * to the  Files plugin.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createEmailUploadInsertionPoint: function (include, btn) {
		return {
			text   : dgettext('plugin_files', 'Add to Files'),
			handler: this.showFilesUploadEmailDialog.createDelegate(this, [btn]),
			scope  : this,
			iconCls: 'icon_files_category'
		};
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel file chooser panel}.
	 *
	 * @param btn
	 */
	showFilesDownloadAttachmentDialog: function (btn) {
		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['common.dialog.attachments.files'], btn.record, {
			title  : dgettext('plugin_files', 'Add attachment from Files'),
			modal  : true,
			manager: Ext.WindowMgr
		});
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel folder chooser panel}.
	 *
	 * @param btn
	 */
	showFilesUploadAttachmentDialog: function (btn) {

		var attachmentRecord = btn.records;
		var attachmentStore = attachmentRecord.store;

		var store = attachmentStore.getParentRecord().get('store_entryid');
		var entryid = attachmentStore.getAttachmentParentRecordEntryId();
		var attachNum = [1];
		if (attachmentRecord.get('attach_num') != -1) {
			attachNum[0] = attachmentRecord.get('attach_num');
		} else {
			attachNum[0] = attachmentRecord.get('tmpname');
		}
		var dialog_attachments = attachmentStore.getId();
		var filename = attachmentRecord.get('name');

		var jsonRecords = [{
			entryid           : entryid,
			store             : store,
			attachNum         : attachNum,
			dialog_attachments: dialog_attachments,
			filename          : filename
		}];

		var configRecord = {
			items: jsonRecords,
			type : "attachment",
			count: jsonRecords.length
		};

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles'], configRecord, {
			manager: Ext.WindowMgr
		});
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel folder chooser panel}.
	 *
	 * @param btn
	 */
	showFilesUploadEmailDialog: function (btn) {

		/* store the eml to a temporary folder and prepare it for uploading */
		var emailRecord = btn.records;

		var records = [].concat(emailRecord);

		var jsonRecords = new Array();
		for (var i = 0, len = records.length; i < len; i++) {
			jsonRecords[i] = {
				store   : records[i].get('store_entryid'),
				entryid : records[i].get('entryid'),
				filename: (Ext.isEmpty(records[i].get('subject')) ? dgettext('plugin_files', 'Untitled') : records[i].get('subject')) + ".eml"
			};
		}

		var configRecord = {
			items: jsonRecords,
			type : "mail",
			count: jsonRecords.length
		};

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles'], configRecord, {
			manager: Ext.WindowMgr
		});
	},

	/**
	 * This method registers the Files module names to the main WebApp.
	 */
	registerModules: function () {
		Zarafa.core.ModuleNames['IPM.FILESACCOUNT'] = {
			list: 'filesaccountmodule',
			item: 'filesaccountmodule'
		}
	},

	/**
	 * Bid for the type of shared component and the given record.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @returns {Number}
	 */
	bidSharedComponent: function (type, record) {
		var bid = -1;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['filesplugin.accountedit']:
				bid = 2;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featurequotainfo']:
				bid = 2;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featureversioninfo']:
				bid = 2;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles']:
				bid = 1;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.files']:
				if (record instanceof Zarafa.core.data.IPMRecord) {
					if (record.supportsAttachments()) {
						bid = 1;
					}
				}
				break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent: function (type, record) {
		var component;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['filesplugin.accountedit']:
				component = Zarafa.plugins.files.settings.ui.AccountEditContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featurequotainfo']:
				component = Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featureversioninfo']:
				component = Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.files']:
				component = Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles']:
				component = Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel;
				break;
		}

		return component;
	}
});

/**
 * This code gets executed after the WebApp has loaded.
 * It hooks the plugin to the WebApp.
 */
Zarafa.onReady(function () {
	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name             : 'files',
		displayName      : dgettext('plugin_files', 'Files Plugin'),
		about            : Zarafa.plugins.files.ABOUT,
		allowUserDisable : true,
		pluginConstructor: Zarafa.plugins.files.FilesPlugin
	}));
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.AccountRecordFields
 *
 * These fields will be available in all 'IPM.FilesAccount' type messages.
 */
Zarafa.plugins.files.data.AccountRecordFields = [
	{name: 'id'},
	{name: 'name'},
	{name: 'status'},
	{name: 'status_description'},
	{name: 'backend'},
	{name: 'backend_config'},
	{name: 'backend_features'},
	{name: 'account_sequence', type: 'number'},
	{name: 'cannot_change', type: 'boolean', defaultValue: false}
];

/**
 * @class Zarafa.plugins.files.data.AccountRecordStatus
 *
 * This object contains all valid status codes that an account can have.
 */
Zarafa.plugins.files.data.AccountRecordStatus = {
	OK     : "ok",
	ERROR  : "err",
	NEW    : "new",
	UNKNOWN: "unk"
};

/**
 * @class Zarafa.plugins.files.data.AccountRecordStatus
 *
 * This object contains all available feature codes that an account can have.
 */
Zarafa.plugins.files.data.AccountRecordFeature = {
	QUOTA       : "Quota",
	VERSION_INFO: "VersionInfo",
	SHARING     : "Sharing",
	STREAMING   : "Streaming",
	OAUTH       : "OAUTH"
};

/**
 * @class Zarafa.plugins.files.data.AccountRecord
 * @extends Zarafa.core.data.IPMRecord
 */
Zarafa.plugins.files.data.AccountRecord = Ext.extend(Zarafa.core.data.IPMRecord, {

	/**
	 * Applies all data from an {@link Zarafa.plugins.files.data.AccountRecord AccountRecord}
	 * to this instance. This will update all data.
	 *
	 * @param {Zarafa.plugins.files.data.AccountRecord} record The record to apply to this
	 * @return {Zarafa.plugins.files.data.AccountRecord} this
	 */
	applyData: function (record) {
		this.beginEdit();

		Ext.apply(this.data, record.data);
		Ext.apply(this.modified, record.modified);

		this.dirty = record.dirty;

		this.endEdit(false);

		return this;
	},

	/**
	 * Check if the account support the given feature.
	 *
	 * @param featureName Should be one of Zarafa.plugins.files.data.AccountRecordFeature children.
	 * @return {boolean}
	 */
	supportsFeature: function (featureName) {
		var features = this.get('backend_features');

		var i = features.length;
		while (i--) {
			if (features[i] === featureName) {
				return true;
			}
		}

		return false;
	},

	/**
	 * Check if the account support the given feature.
	 *
	 * @param featureName Should be one of Zarafa.plugins.files.data.AccountRecordFeature children.
	 * @return {boolean}
	 */
	renewOauthToken: function () {
		if(!this.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.OAUTH)) {
			return false;
		}

		// show the frontend panel
		Zarafa.plugins.files.backend[this.get('backend')].data.OAUTH.reAuthenticate(this.get('id'));

		return true;
	}
});


Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.FilesAccount', Zarafa.plugins.files.data.AccountRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM.FilesAccount', Zarafa.plugins.files.data.AccountRecord);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.AccountStore
 * @extends Zarafa.core.data.ListModuleStore
 * @xtype filesplugin.accountstore
 *
 * This store will hold all Files accounts that a user owns.
 */
Zarafa.plugins.files.data.AccountStore = Ext.extend(Zarafa.core.data.ListModuleStore, {

	/**
	 * @constructor
	 */
	constructor: function () {
		Zarafa.plugins.files.data.AccountStore.superclass.constructor.call(this, {
			preferredMessageClass: 'IPM.FilesAccount',
			autoSave             : true,
			actionType           : Zarafa.core.Actions['list'],
			defaultSortInfo      : {
				field    : 'account_sequence',
				direction: 'asc'
			}
		});

		// add custom event
		this.addEvents('reorder'); // reorder has two arguments: panel a and panel b, those got swapped
	}
});

Ext.reg('filesplugin.accountstore', Zarafa.plugins.files.data.AccountStore);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.Actions
 * @singleton
 *
 * Common actions which can be used within {@link Ext.Button buttons}
 * or other {@link Ext.Component components} with action handlers.
 */
Zarafa.plugins.files.data.Actions = {

	/**
	 * The internal 'iframe' which is hidden from the user, which is used for downloading
	 * attachments. See {@link #doOpen}.
	 *
	 * @property
	 * @type Ext.Element
	 */
	downloadFrame: undefined,

	/**
	 * Converts received file information to attachment record.
	 *
	 * @param {Object} record
	 * @private
	 */
	convertDownloadedFileInfoToAttachmentRecord: function (record) {
		var attachmentRecord = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.MAPI_ATTACH);

		attachmentRecord.set('tmpname', record.tmpname);
		attachmentRecord.set('name', record.name);
		attachmentRecord.set('size', record.size);
		return attachmentRecord;
	},

	/**
	 * Open a Panel in which a new {@link Zarafa.core.data.IPMRecord record} can be
	 * further edited.
	 *
	 * @param {Zarafa.core.data.IPMRecord} emailRecord The email record that will be edited.
	 * @param {Array} records Filerecords that will be added as attachments.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateMailContent: function (emailRecord, records, config) {
		var attachmentStore = emailRecord.getAttachmentStore();
		var attachmentRecord = null;
		var isHtml = emailRecord.get("isHTML");

		var server = container.getServerConfig();

		var max_attachment_size = server.getMaxAttachmentSize();

		var large_files_enabled = (typeof server.isLargeFilesEnabled === 'function') ? server.isLargeFilesEnabled() : false;

		Ext.each(records, function (record, index) {
			attachmentRecord = this.convertDownloadedFileInfoToAttachmentRecord(record);
			attachmentStore.add(attachmentRecord);
			if (large_files_enabled && attachmentRecord.get('size') > max_attachment_size) {

				var currBody = emailRecord.getBody();
				var lfLink = attachmentRecord.getLargeFileLink(isHtml);
				emailRecord.setBody(currBody + lfLink, isHtml);
			}
		}, this);

		Zarafa.core.data.UIFactory.openCreateRecord(emailRecord, config);
	},

	/**
	 * Open a Panel in which a new {@link Zarafa.core.data.IPMRecord record} can be
	 * further edited.
	 *
	 * @param {Zarafa.mail.MailContextModel} model Context Model object that will be used
	 * to {@link Zarafa.mail.MailContextModel#createRecord create} the E-Mail.
	 * @param {Zarafa.addressbook.AddressBookRecord} contacts One or more contact records.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateMailContentForContacts: function (model, contacts, config) {
		var emailRecord = container.getContextByName("mail").getModel().createRecord();

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['zarafa.plugins.files.attachdialog'], undefined, {
			title      : String.format(dgettext('plugin_files', 'Add attachment from {0}'), container.getSettingsModel().get('zarafa/v1/plugins/files/button_name')),
			emailrecord: emailRecord,
			manager    : Ext.WindowMgr
		});

		var recipientStore = emailRecord.getRecipientStore();
		var tasks = [];

		contacts = Ext.isArray(contacts) ? contacts : [contacts];
		for (var i = 0, len = contacts.length; i < len; i++) {
			var contact = contacts[i];

			if (contact.isOpened()) {

				var recipient = contact.convertToRecipient(Zarafa.core.mapi.RecipientType.MAPI_TO, true);
				recipientStore.add(recipient);
			} else {

				tasks.push({

					fn: function () {

						var contactRecord = contact;
						return function (panel, record, task, callback) {
							var fn = function (store, record) {
								if (record === contactRecord) {
									store.un('open', fn, task);
									var recipient = contactRecord.convertToRecipient(Zarafa.core.mapi.RecipientType.MAPI_TO, true);
									recipientStore.add(recipient);
									callback();
								}
							};

							contactRecord.getStore().on('open', fn, task);
							contactRecord.open();
						}

					}()
				});
			}
		}

		config = Ext.applyIf(config || {}, {
			recordComponentPluginConfig: {
				loadTasks: tasks
			}
		});

		Zarafa.core.data.UIFactory.openCreateRecord(emailRecord, config);
	},

	/**
	 * Create a new item...
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model Context Model.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateFilesContent: function (model, config) {
		var record = model.createRecord();
		Zarafa.core.data.UIFactory.openCreateRecord(record, config);
	},

	/**
	 * Open a Panel in which the {@link Zarafa.core.data.IPMRecord record}
	 * can be viewed, or further edited.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The records to open
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openFilesContent: function (records, config) {
		if (records.length == 1 && records[0].get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
			var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(records[0].get('id'));
			var navpanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
			var store = Zarafa.plugins.files.data.ComponentBox.getStore();

			store.loadPath(records[0].get('id'));
			Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.unselectAllNavPanels();
			var nodeToSelect = navpanel.getNodeById(records[0].get('id'));
			if (Ext.isDefined(nodeToSelect)) {
				navpanel.selectNode(nodeToSelect);
			}
		} else if (records.length > 0) {
			this.downloadItem(records);
		}
	},

	/**
	 * Function is used to delete files or folders.
	 *
	 * @param {Array} records
	 */
	deleteRecords: function (records) {
		var allowDelete = true;
		var folderCount = 0;
		var fileCount = 0;
		var firstFileName, firstFolderName;

		Ext.each(records, function (record) {
			if (record.get('id') === (container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/") || record.get('filename') === "..") {
				allowDelete = false;
			}
			if (record.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
				folderCount += 1;
				if (!firstFolderName) {
					firstFolderName = record.get('filename');
				}
			} else if (record.get('type') === Zarafa.plugins.files.data.FileTypes.FILE) {
				fileCount += 1;
				if (!firstFileName) {
					firstFileName = record.get('filename');
				}
			}
		}, this);

		var askOnDelete = container.getSettingsModel().get('zarafa/v1/contexts/files/ask_before_delete');

		if (allowDelete) {
			if (askOnDelete) {
				Ext.MessageBox.confirm(dgettext('plugin_files', 'Confirm deletion'),
					this.createDeletionMessage(fileCount, firstFileName, folderCount, firstFolderName),
					function (button) {
						if (button === 'yes') {
							this.doDelete(records);
						}
					}, this);
			} else {
				this.doDelete(records);
			}
		}
	},

	/**
	 * Create a proper message for the confirm deletion message box
	 * @param {Integer} fileCount number of files to delete
	 * @param {String} fileName name of the file to be deleted
	 * @param {Integer} folderCount number of folders to delete
	 * @param {String} folderName name of the folder to be deleted
	 * @return {String} the string to be shown in the delete confirmation dialog
	 */
	createDeletionMessage: function(fileCount, fileName, folderCount, folderName) {
		//single file
		if (fileCount === 1 && folderCount === 0) {
			return String.format(dgettext('plugin_files', 'Are you sure you want to delete {0}?'), fileName);
		}
		//single folder
		if (fileCount === 0 && folderCount === 1) {
			 return String.format(dgettext('plugin_files', 'Are you sure you want to delete {0} and all of its contents?'), folderName);
		}
		//multiple files
		if (fileCount >= 1 && folderCount === 0) {
			 return String.format(dgettext('plugin_files', 'Are you sure you want to delete {0} files?'), fileCount);
		}
		//multiple folders
		if (fileCount === 0 && folderCount >= 1) {
			return String.format(dgettext('plugin_files', 'Are you sure want to delete {0} folders and all of their contents?'), folderCount);
		}
		//multiple files and folders
		if (fileCount !== 0 && folderCount !== 0) {
			return dgettext('plugin_files', 'Are you sure want to delete the selected items and all of their contents?');
		}
	},

	/**
	 * Delete the selected files.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord|Array} records The records that must be deleted.
	 * @private
	 */
	doDelete: function (records)
	{
		var ids = [];
		Ext.each(records, function (record) {
			ids.push({
				id: record.get('id')
			});
		});

		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'delete',
			{
				records: ids
			},
			new Zarafa.plugins.files.data.ResponseHandler({
				successCallback: this.deleteDone.createDelegate(this, [ids], true)
			})
		);
	},

	/**
	 * Function gets called after files were removed.
	 *
	 * @param {Object} response
	 * @param {Array} recordids
	 * @private
	 */
	deleteDone: function (response, recordids) {
		var store = Zarafa.plugins.files.data.ComponentBox.getStore();

		Ext.each(recordids, function (record) {
			var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(record.id);
			var navpanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
			var nodeToDelete = navpanel.getNodeById(record.id);
			if (Ext.isDefined(nodeToDelete) && nodeToDelete.rendered) {
				nodeToDelete.remove(true);
			}

			// delete the file from the grid
			var path = Zarafa.plugins.files.data.Utils.File.getDirName(record.id) + '/';
			if (Zarafa.plugins.files.data.ComponentBox.getStore().getPath() === path) {

				var rec = store.getById(record.id);
				store.on("update", this.doRefreshIconView, this, {single: true});
				store.remove(rec);
				store.commitChanges();
			}
		});


		if (container.getCurrentContext().getCurrentView() === Zarafa.plugins.files.data.Views.ICON) {
			Zarafa.plugins.files.data.ComponentBox.getItemsView().refresh();
		}
	},

	/**
	 * Refreshes the left navigator tree.
	 */
	refreshNavigatorTree: function () {
		var store = Zarafa.plugins.files.data.ComponentBox.getStore();
		var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(store.getPath());
		var navpanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);

		if (Ext.isDefined(navpanel)) {
			navpanel.refreshNode(store.getPath());
		}
	},

	/**
	 * Create the sharing dialog.
	 *
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 * @param {Array} records Selected filerecords
	 */
	createShareDialog: function (records, config) {
		config = Ext.applyIf(config || {}, {
			modal  : true,
			records: records
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * This function is called after the {@Zarafa.plugins.files.data.FilesStore} has loaded the target folder.
	 * It will check if one of the selected files already exists in the store. If there is a duplicate file
	 * a warning will be shown.
	 *
	 * @param {Object} response
	 * @param {Object} records The Records that were loaded or a simple string
	 * @param {Object} destination record
	 * @param {String} mode
	 * @private
	 */
	checkForDuplicateDone: function (response, records, destination, mode) {
		switch (mode) {
			case 'rename':
				if (response.duplicate === true) {
					this.msgWarning(dgettext('plugin_files', 'This name already exists'));
				} else if (!Zarafa.plugins.files.data.Utils.File.isValidFilename(records)) {
					this.msgWarning(dgettext('plugin_files', 'Incorrect name'));
				} else {
					this.doRename(records, destination);
				}
				break;
			case 'move':
			default:
				if (response.duplicate === true) {
					Ext.MessageBox.confirm(
						dgettext('plugin_files', 'Confirm overwrite'),
						dgettext('plugin_files', 'File already exists. Do you want to overwrite it?'),
						this.doMoveRecords.createDelegate(this, [records, destination], true),
						this
					);
				} else {
					this.doMoveRecords("yes", null, null, records, destination);
				}
				break;
		}
	},

	/**
	 * Create a new Folder in {@link Zarafa.core.data.IPMRecord node}.
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model Context Model.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 * @param {String} path The destination path in which the new folder will be created.
	 */
	createFolder: function (model, config, path) {
		if (!Ext.isDefined(path) || Ext.isEmpty(path)) {
			path = model.getStore().getPath();
		}

		config = Ext.applyIf(config || {}, {
			modal  : true,
			accountFilter : Zarafa.plugins.files.data.Utils.File.getAccountId(path),
			selectedFolderId : path
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Callback for the {@link Zarafa.plugins.files.data.FilesRecordStore#load} event.
	 * This function will refresh the view of the main panel.
	 */
	doRefreshIconView: function () {
		if (Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentView() === Zarafa.plugins.files.data.Views.ICON) {
			Zarafa.plugins.files.data.ComponentBox.getItemsView().refresh();
		}
	},

	/**
	 * Move specified records to the new folder.
	 *
	 * @param {Array} records array of records
	 * @param {Object} destination record or treenode
	 * @param {Boolean} overwrite boolean flag - if true, existing dst records will be overwritten, default: true
	 */
	moveRecords: function (records, destination, overwrite) {
		overwrite = Ext.isDefined(overwrite) ? overwrite : false;

		if (!Ext.isDefined(destination.data)) {
			var parent = destination.parentNode;
			destination = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.ZARAFA_FILES, {
				id            : destination.id,
				entryid       : destination.id,
				parent_entryid: Ext.isDefined(parent) ? "/" : parent.id
			}, destination.id);
		}

		if (!overwrite) {
			var ids = [];
			Ext.each(records, function (record) {
				ids.push({
					id      : record.get('id'),
					isFolder: (record.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER)
				});
			});

			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'checkifexists',
				{
					records    : ids,
					destination: destination.get('id')
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.checkForDuplicateDone.createDelegate(this, [records, destination, 'move'], true)
				})
			);
		} else {
			this.doMoveRecords("yes", null, null, ids, destination);
		}
	},

	/**
	 * This function will actually move the given files to a new destination.
	 *
	 * @param {String} button If button is set it must be "yes" to move files.
	 * @param {String} value Unused
	 * @param {Object} options Unused
	 * @param {Array} files Array of records
	 * @param {Object} destination Destination folder
	 * @private
	 */
	doMoveRecords: function (button, value, options, files, destination) {
		if (!Ext.isDefined(button) || button === "yes") {
			var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(destination.get('id'));
			var navpanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
			var destinationNode = navpanel.getNodeById(destination.get('id'));

			Ext.each(files, function (record) {
				record.beginEdit();
				record.moveTo(destination);
				record.set("deleted", true);
				record.endEdit();
				record.commit();

				if (Ext.isDefined(navpanel)) {
					var nodeToDelete = navpanel.getNodeById(record.get('id'));
					if (Ext.isDefined(nodeToDelete) && nodeToDelete.rendered) {
						var deleted = nodeToDelete.remove();

						if (Ext.isDefined(destinationNode) && destinationNode.rendered && deleted.attributes.isFolder === true) {

							var has_children = deleted.has_children ? true : deleted.childNodes.length > 0;
							var node = {
								id          : (destinationNode.attributes.id + deleted.text + '/'),
								text        : deleted.text,
								isFolder    : true,
								has_children: has_children,
								expanded    : !has_children,
								loaded      : false,
								iconCls     : 'icon_folder_note',
								filename    : deleted.text,
								leaf        : false
							};
							destinationNode.appendChild(node);
						}
					}
				}
			});

			Zarafa.plugins.files.data.ComponentBox.getStore().filter("deleted", false);

			if (Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentView() === Zarafa.plugins.files.data.Views.ICON) {
				Zarafa.plugins.files.data.ComponentBox.getItemsView().refresh();
			}
		} else {

			Ext.each(files, function (record) {
				record.setDisabled(false);
			});
		}
	},

	/**
	 * Open a rename dialog and rename the item
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 */
	openRenameDialog: function (model, record) {
		Ext.MessageBox.prompt(
			dgettext('plugin_files', 'Rename'),
			dgettext('plugin_files', 'Please enter a new name'),
			this.doCheckRenameDuplicate.createDelegate(this, [record], true),
			this,
			false,
			record.get('filename'));
	},

	/**
	 * Check if the new name already exists
	 *
	 * @param {String} button The value of the button
	 * @param {String} text Inputfield value, new name
	 * @param {Object} options Unused
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 * @private
	 */
	doCheckRenameDuplicate: function (button, text, options, record) {
		if (button === "ok") {
			var path = Zarafa.plugins.files.data.Utils.File.getDirName(record.get('id')) + '/';
			var isFolder = /\/$/.test(record.get('id')) ? "/" : "";

			var ids = [{
				id      : path + text + isFolder,
				isFolder: isFolder === "/"
			}];

			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'checkifexists',
				{
					records    : ids,
					destination: path
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.checkForDuplicateDone.createDelegate(this, [text, record, 'rename'], true)
				})
			);
		}
	},

	/**
	 * Rename a record on the server
	 *
	 * @param {String} text Inputfield value, new name
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 * @private
	 */
	doRename: function (text, record) {
		var recordID = record.get('id');
		var path = Zarafa.plugins.files.data.Utils.File.getDirName(recordID) + '/';
		var isFolder = /\/$/.test(recordID) ? "/" : "";

		var new_id = path + text + isFolder;
		var new_record_data = record.copy().data;
		new_record_data.id = new_id;
		new_record_data.filename = text;
		new_record_data.virtualRecord = false;

		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'rename',
			{
				entryid: record.id,
				props  : new_record_data
			},
			new Zarafa.plugins.files.data.ResponseHandler({
				successCallback: this.renameDone.createDelegate(this, [text, record], true)
			})
		);
	},

	/**
	 * This function is called after the record has been renamed on the server.
	 *
	 * @param {Object} response
	 * @param {String} text Inputfield value, new name
	 * @param {Zarafa.plugins.files.data.FilesRecord} record file record
	 * @private
	 */
	renameDone: function (response, text, record)
	{
		var recordID = record.id;
		var path = Zarafa.plugins.files.data.Utils.File.getDirName(recordID) + '/';
		var isFolder = /\/$/.test(recordID) ? "/" : "";

		var old_id = recordID;
		var new_id = path + text + isFolder;

		// Update the relevant items
		// - navigator tree node and its children if we have a folder
		// - the main view of the renamed item if it is shown
		var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(old_id);
		var renamedNode = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID).getNodeById(old_id);
		if (Ext.isDefined(renamedNode) && renamedNode.rendered) {
			renamedNode.setId(new_id);
			renamedNode.setText(text);
			renamedNode.attributes.filename = text;
			var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
			var re = new RegExp('^' + old_id);
			if (re.test(navPanel.nodeToSelect)) {
				navPanel.nodeToSelect = new_id;
			}
			navPanel.refreshNode(new_id, true);
		}

		var store = Zarafa.plugins.files.data.ComponentBox.getStore();
		if (store.getPath() === path) {
			store.on("update", this.doRefreshIconView, this, {single: true});
			store.reload({noNavBar: true});
		}
	},

	/**
	 * Does a full reload of the current directory.
	 */
	reloadCurrentDir: function () {
		Zarafa.plugins.files.data.ComponentBox.getStore().reload();
	},

	/**
	 * Reloads the root node of the Treepanel.
	 */
	reloadNavigatorTree: function () {
		var accountStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		accountStore.each(function (account, index) {
			var tree = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(account.get('id'));

			if (Ext.isDefined(tree)) {
				tree.setRootNode({
					nodeType: 'async',
					text    : '/',
					id      : '#R#',
					expanded: true
				});
			}
		});
	},

	/**
	 * Download the selected items from files.
	 *
	 * @param {Array} records An array of ids
	 */
	downloadItem: function (records) {
		container.getNotifier().notify('info.files', dgettext('plugin_files', 'Downloading'), dgettext('plugin_files', 'Download started... please wait!'));

		var downloadFrame = Ext.getBody().createChild({
			tag: 'iframe',
			cls: 'x-hidden'
		});
		if (records.length == 1) {
			var url = this.getDownloadLink(records[0], false);
			downloadFrame.dom.contentWindow.location = url;
		} else if (records.length > 1) {
			var url = this.getDownloadLinkForMultipleFiles(records, false);
			downloadFrame.dom.contentWindow.location = url;
		}
	},

	/**
	 * Upload the given files to the files backend. This method will use the async XMLHttpRequest to
	 * upload the files to the server.
	 *
	 * @param {Array} files An array of files
	 * @param {Object|string} store The destination {@Zarafa.plugins.files.data.FilesStore} or a simple {string} path
	 */
	uploadAsyncItems: function (files, store) {
		var destination;
		if ((typeof store) == "string") {
			destination = store;
		} else {
			destination = store.getPath();
		}

		// build the ids:
		var ids = [];
		var fileTooLarge = false;

		Ext.each(files, function (file) {
			if (file.size > Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) {
				fileTooLarge = true;
			}
			var id = destination + file.name;
			ids.push({
				id      : id,
				isFolder: false
			});
		});

		if (fileTooLarge) {
			Zarafa.common.dialogs.MessageBox.show({
				title  : dgettext('plugin_files', 'Error'),
				msg    : String.format(dgettext('plugin_files', 'At least one file is too large! Maximum allowed filesize: {0}.'), Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize())),
				icon   : Zarafa.common.dialogs.MessageBox.ERROR,
				buttons: Zarafa.common.dialogs.MessageBox.OK
			});
		} else {
			// check for duplicates
			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'checkifexists',
				{
					records    : ids,
					destination: destination
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.checkForExistingFilesDone.createDelegate(this, [files, destination, this.doAsyncUpload], true)
				})
			);
		}
	},

	/**
	 * Actually uploads all the files to the server.
	 *
	 * @param {Zarafa.common.dialogs.MessageBox.addCustomButtons} button
	 * @param {Array} files An array of files
	 * @param destination record id.
	 */
	doAsyncUpload: function (button, files, destination) {
		if (button === "overwrite" || button === "keepboth") {
			var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog'];
			Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, {
				files : files,
				destination : destination,
				keepBoth : button === "keepboth",
				manager : Ext.WindowMgr,
				callbackAllDone : this.uploadDone.bind(this)
			});
		}
	},

	/**
	 * Callback if the upload has completed.
	 *
	 * @param files
	 * @param destination
	 * @param xhr
	 */
	uploadDone: function (files, destination, xhr) {
		var store = Zarafa.plugins.files.data.ComponentBox.getStore();
		if (store.getPath() === destination) {
			store.reload();
		} else {
			this.updateCache(destination);
		}
	},

	/**
	 * This function is called after the {@Zarafa.plugins.files.data.FilesStore} has loaded the target folder.
	 * It will check if one of the selected files already exists in the store. If there is a duplicate file
	 * a warning will be shown.
	 *
	 * @param {Object} response
	 * @param {File|Array} files The files that should be uploaded
	 * @param {string} destination record id
	 * @param {Function} callback function which triggers {@link #doAsyncUpload} function.
	 * @private
	 */
	checkForExistingFilesDone: function (response, files, destination, callback) {
		if (response.duplicate === true) {
			Zarafa.common.dialogs.MessageBox.addCustomButtons({
				title : dgettext('plugin_files', 'Confirm overwrite'),
				icon: Ext.MessageBox.QUESTION,
				msg : dgettext('plugin_files', 'File already exists. Do you want to overwrite it?'),
				fn : callback.createDelegate(this, [files, destination], true),
				customButton : [{
					text :  dgettext('plugin_files', 'Keep both'),
					name : 'keepboth'
				}, {
					text :  dgettext('plugin_files', 'Overwrite'),
					name : 'overwrite'
				}, {
					text :  dgettext('plugin_files', 'Cancel'),
					name : 'cancel'
				}],
				scope : this
			});
		} else {
			callback.createDelegate(this, ["overwrite", files, destination])();
		}
	},

	/**
	 * Returns a download link for the client.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord} record a file record
	 * @param {Boolean} inline (optional)
	 * @return {String}
	 */
	getDownloadLink: function (record, inline) {
		return (Ext.isDefined(inline) && inline == false) ? record.getAttachmentUrl() : record.getInlineImageUrl();
	},

	/**
	 * Returns a download link for the client to download multiple items.
	 *
	 * @param {Array} records a array of {Zarafa.plugins.files.data.FilesRecord}
	 * @return {String}
	 */
	getDownloadLinkForMultipleFiles: function (records) {
		var link = "";

		var url = document.URL;
		link = url.substring(0, url.lastIndexOf('/') + 1);

		link += "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_file";
		Ext.each(records, function (record, index) {
			link = Ext.urlAppend(link, "ids[" + index + "]=" + encodeURIComponent(record.get("id")));
		});

		link = Ext.urlAppend(link, "inline=false");

		return link;
	},

	/**
	 * This will display a messagebox with warning icons to the user.
	 *
	 * @param {String} errorMessage The error message to display
	 */
	msgWarning: function (errorMessage) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : dgettext('plugin_files', 'Warning'),
			msg    : errorMessage,
			icon   : Zarafa.common.dialogs.MessageBox.WARNING,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});
	},

	/**
	 * Event handler called when the "use Zarafa credentials" checkbox has been modified
	 *
	 * @param {Ext.form.CheckBox} checkbox Checkbox element from which the event originated
	 * @param {Boolean} checked State of the checkbox
	 * @private
	 */
	onCheckCredentials: function (checkbox, checked) {
		if (checked) {
			this.usernameField.hide();
			this.usernameField.setValue("");
			this.usernameField.label.hide();
			this.usernameField.allowBlank = true;
                        this.usernameField.validate();
			this.passwordField.hide();
			this.passwordField.setValue("");
			this.passwordField.label.hide();
			this.passwordField.allowBlank = true;
                        this.passwordField.validate()
		} else {
			this.usernameField.show();
			this.usernameField.label.show();
			this.usernameField.allowBlank = false;
                        this.usernameField.validate();
			this.passwordField.show();
			this.passwordField.label.show();
			this.passwordField.allowBlank = false;
                        this.passwordField.validate()
		}
	},

	/**
	 * Event handler called when the "use ssl connection" checkbox has been modified.
	 * If checked and the server port is 80, switch it to 443, else if the port is unchecked
	 * and the port is not 80, change it to the default.
	 *
	 * @param {Ext.form.CheckBox} checkbox Checkbox element from which the event originated
	 * @param {Boolean} checked State of the checkbox
	 */
	onCheckSSL: function (checkbox, checked) {
		if (checked && this.portField.getValue() === "80") {
			this.portField.setValue("443");
		} else if (!checked && this.portField.getValue() === "443") {
			this.portField.setValue("80");
		}
	},

	/**
	 * Updates the cache for given record
	 *
	 * @param destination record id.
	 */
	updateCache: function (destination)
	{
		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'updatecache',
			{
				id: destination
			}
		);
	}
};
/**
 * @class Ext.tree.AsyncTreeNode
 * @overrrides Ext.tree.AsyncTreeNode
 *
 * This class overrides Ext.tree.AsyncTreeNode. It adds the functionality to silently load a node without expanding it.
 */
Ext.override(Ext.tree.AsyncTreeNode, {

	/**
	 * This functions loads a node without expanding it.
	 *
	 * @param deep
	 * @param anim
	 * @param callback
	 */
	quietLoad : function(deep, anim, callback) {
		deep = 0;
		if(this.loading){ // if an async load is already running, waiting til it's done
			var timer;
			var f = function(){
				if(!this.loading){ // done loading
					clearInterval(timer);
					//this.expand(deep, anim, callback);
				}
			}.createDelegate(this);
			timer = setInterval(f, 200);
			return;
		}

		if(!this.loaded){
			if(this.fireEvent("beforeload", this) === false){
				return;
			}
			this.loading = true;
			this.ui.beforeLoad(this);
			var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
			if(loader){
				// add a class to hide the loading icon
				this.setCls("x-treenode-load-silent");
				loader.load(this, this.quietLoadComplete.createDelegate(this, [deep, anim, callback]));
				return;
			}
		}
	},

	/**
	 * Callback for the tree loader. It is called on the "load" event.
	 * @param deep
	 * @param anim
	 * @param callback
	 */
	quietLoadComplete : function(deep, anim, callback){
		// remove the silent class again
		this.setCls("");

		if (this.childNodes.length === 0) {
			this.leaf = true;
		}
		this.loading = false;
		this.loaded = true;
		this.ui.afterLoad(this);
		this.fireEvent("load", this);
		this.ui.expand();
	},

	/**
	 * Set the leaf flag for the node
	 * @param {Boolean} value
	 */
	setLeaf: function(value){
		this.leaf = value;
	}
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ComponentBox
 * @singleton
 *
 * The global component box which holds all aliases to important components.
 */
Zarafa.plugins.files.data.ComponentBox = Ext.extend(Object, {

	/**
	 * Get the files context.
	 *
	 * @return {Zarafa.plugins.files.FilesContext}
	 */
	getContext: function () {
		return container.getContextByName("filescontext");
	},

	/**
	 * Get the files context model.
	 *
	 * @return {Zarafa.plugins.files.FilesContextModel}
	 */
	getContextModel: function () {
		return this.getContext().getModel();
	},

	/**
	 * Get the files record store.
	 *
	 * @return {Zarafa.plugins.files.data.FilesRecordStore}
	 */
	getStore: function () {
		return Zarafa.plugins.files.data.singleton.FilesRecordStoreManager.getStore(); // get the default store
	},

	/**
	 * Get the navigatorpanel.
	 *
	 * @return {Zarafa.core.ui.NavigationPanel}
	 */
	getNavigatorPanel: function () {
		return container.getNavigationBar();
	},

	/**
	 * Get the navigator treepanel
	 * @return {Zarafa.plugins.files.ui.NavigatorTreePanel}
	 */
	getNavigatorTreePanel: function (accountID) {
		var navPanel = this.getNavigatorPanel();
		return navPanel['filesNavigatorTreePanel_' + accountID];
	},

	/**
	 * Get the main panel.
	 *
	 * @return {Zarafa.core.ui.MainViewport}
	 */
	getMainPanel: function () {
		try {
			return container.getContentPanel();
		} catch (e) {

			return container.getTabPanel().getItem(0).getActiveItem();
		}
	},

	/**
	 * Get the preview panel.
	 *
	 * @return {Zarafa.plugins.files.ui.FilesPreviewPanel}
	 */
	getPreviewPanel: function () {
		return this.getMainPanel().filesPreview;
	},

	/**
	 * Get the tabpanel.
	 *
	 * @return {Zarafa.core.ui.ContextContainer}
	 */
	getTabPanel: function () {
		return container.getTabPanel();
	},

	/**
	 * Get the tabpanel items.
	 *
	 * @return {Array}
	 */
	getTabPanelItems: function () {
		return this.getTabPanel().items.items;
	},

	/**
	 * Get the files viewpanel.
	 *
	 * @return {Zarafa.core.ui.SwitchViewContentContainer}
	 */
	getViewPanel: function () {
		return this.getMainPanel().viewPanel;
	},

	/**
	 * Get the files gridpanel or iconviewpanel.
	 *
	 * @return {Zarafa.plugins.files.ui.FilesRecordGridView} or {Zarafa.plugins.files.ui.FilesRecordIconView}
	 */
	getItemsView: function () {
		return this.getViewPanel().items.get(0);
	},

	/**
	 * Get the files grid toolbar.
	 *
	 * @return {Zarafa.plugins.files.ui.FilesToolbar}
	 */
	getViewPanelToolbar: function () {
		var viewPanel = this.getMainPanel().filesViewPanel;
		if (viewPanel) {
			return viewPanel.topToolbar;
		} else {
			return undefined;
		}
	}
});

Zarafa.plugins.files.data.ComponentBox = new Zarafa.plugins.files.data.ComponentBox();Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.DataModes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different data modes of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.DataModes = Zarafa.core.Enum.create({

	/**
	 * DataMode: all
	 *
	 * @property
	 * @type Number
	 */
	ALL: 0
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FileTypes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different file types of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.FileTypes = Zarafa.core.Enum.create({

	/**
	 * Filetype: folder
	 *
	 * @property
	 * @type Number
	 */
	FOLDER: 0,

	/**
	 * Filetype: file
	 *
	 * @property
	 * @type Number
	 */
	FILE: 1
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.FilesRecordFields
 *
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.core.data.IPMRecord IPMRecord} object.
 * These fields will be available in all 'IPM.Files' type messages.
 */
Zarafa.plugins.files.data.FilesRecordFields = [
	{name: 'id'},
	{name: 'path'},
	{name: 'type', type: 'int', defaultValue: Zarafa.plugins.files.data.FileTypes.FOLDER},
	{name: 'filename'},
	{name: 'isshared', type: 'boolean', defaultValue: false},
	{name: 'sharedid'},
	{name: 'lastmodified', type: 'int', defaultValue: null},
	{name: 'message_size', type: 'int', defaultValue: 0},
	{name: 'deleted', type: 'boolean', defaultValue: false},
	{name: 'virtualRecord', type: 'boolean', defaultValue: false} // this flag will tell the backend to ignore the record
];

/**
 * @class Zarafa.plugins.files.data.FilesRecord
 * @extends Zarafa.core.data.IPMRecord
 */
Zarafa.plugins.files.data.FilesRecord = Ext.extend(Zarafa.core.data.IPMRecord, {

	/**
	 * @cfg {Boolean} Record state.
	 */
	disabled: false,

	/**
	 * Applies all data from an {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 * to this instance. This will update all data.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord} record The record to apply to this
	 * @return {Zarafa.plugins.files.data.FilesRecord} this
	 */
	applyData: function (record) {
		this.beginEdit();

		Ext.apply(this.data, record.data);
		Ext.apply(this.modified, record.modified);

		this.dirty = record.dirty;

		this.endEdit(false);

		return this;
	},

	/**
	 * Builds and returns inline image URL to download inline images,
	 * it uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 *
	 * @return {String} URL for downloading inline images.
	 */
	getInlineImageUrl: function () {
		return container.getBasePath() + "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_file&" + Ext.urlEncode({
			id    : this.get('id'),
			inline: true
		});
	},

	/**
	 * Builds and returns attachment URL to download attachment,
	 * it uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 *
	 * @return {String} URL for downloading attachment.
	 */
	getAttachmentUrl: function () {
		return container.getBasePath() + "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_file&" + Ext.urlEncode({
			id    : this.get('id'),
			inline: false
		});
	},

	/**
	 * Builds and returns inline thumbnail image URL to download show images,
	 * it uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 *
	 * @param {Integer} width Width of the thumbnail
	 * @param {Integer} height Height of the thumbnail
	 * @return {String} URL for downloading inline images.
	 */
	getThumbnailImageUrl: function (width, height) {
		var link = "";

		link += "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_file&" + Ext.urlEncode({
			id    : this.get('id'),
			inline: false,
			thumb : true,
			width : width,
			height: height
		});

		return link;
	},

	/**
	 * Set the disabled flag.
	 *
	 * @param {Boolean} state
	 */
	setDisabled: function (state) {
		this.disabled = state;
	},

	/**
	 * Get the disabled flag.
	 *
	 * @return {Boolean}
	 */
	getDisabled: function () {
		return this.disabled;
	},

	/**
	 * Get the account object for the record.
	 *
	 * @return {Zarafa.plugins.files.data.AccountRecord} an IPM.FilesAccount record
	 */
	getAccount: function () {
		var accId = Zarafa.plugins.files.data.Utils.File.getAccountId(this.get('id'));
		var store = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		// look up the account
		var account = store.getById(accId);

		return account;
	}
});

Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_FILES');

Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.Files', Zarafa.plugins.files.data.FilesRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM.Files', Zarafa.plugins.files.data.FilesRecord);

Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, Zarafa.plugins.files.data.FilesRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, Zarafa.plugins.files.data.FilesRecord);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesRecordStore
 * @extends Zarafa.core.data.ListModuleStore
 *
 * The FilesStore class provides a way to connect the 'filesbrowsermodule' in the server back-end to an
 * Ext.grid.GridPanel object. It provides a means to retrieve files listings asynchronously.
 * The store has to be initialised with a store Id, which corresponds (somewhat confusingly) to
 * a MAPI store id. The FilesStore object, once instantiated, will be able to retrieve and list
 * files from a single specific store only.
 *
 * @constructor
 */
Zarafa.plugins.files.data.FilesRecordStore = Ext.extend(Zarafa.core.data.ListModuleStore, {

	/**
	 * The root path which is loaded.
	 *
	 * @property
	 * @type String
	 * @private
	 */
	rootID: undefined,

	/**
	 * @constructor
	 */
	constructor: function () {
		this.rootID = "#R#";

		Zarafa.plugins.files.data.FilesRecordStore.superclass.constructor.call(this, {
			preferredMessageClass: 'IPM.Files',
			autoSave             : true,
			actionType           : Zarafa.core.Actions['list'],
			defaultSortInfo      : {
				field    : 'filename',
				direction: 'asc'
			},
			baseParams           : {
				id: this.rootID,
				reload: false
			},
			listeners            : {
				load: this.onLoad,
				exception: this.onLoadException
			}
		});
	},

	/**
	 * Triggers the loading of the specified path.
	 *
	 * @param {String} path to load
	 */
	loadPath: function (path) {
		this.rootID = path;

		var params = {
			id: this.rootID
		};
		this.load({params: params});
	},

	/**
	 * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
	 * <br><p>Notes:</p><div class="mdetail-params"><ul>
	 * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
	 * loaded. To perform any post-processing where information from the load call is required, specify
	 * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
	 * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
	 * properties in the <code>options.params</code> property to establish the initial position within the
	 * dataset, and the number of Records to cache on each read from the Proxy.</li>
	 * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
	 * will be automatically included with the posted parameters according to the specified
	 * <code>{@link #paramNames}</code>.</li>
	 * </ul></div>
	 * @param {Object} options An object containing properties which control loading options:<ul>
	 * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
	 * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
	 * <code>{@link #baseParams}</code> of the same name.</p>
	 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
	 * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
	 * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
	 * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
	 * <li>options : Options object from the load call.</li>
	 * <li>success : Boolean success indicator.</li></ul></p></div></li>
	 * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
	 * to the Store object)</p></div></li>
	 * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
	 * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
	 * </ul>
	 * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
	 * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
	 */
	load : function(options)
	{
		if (!Ext.isObject(options)) {
			options = {};
		}

		if (!Ext.isObject(options.params)) {
			options.params = {};
		}

		// By default 'load' must cancel the previous request.
		if (!Ext.isDefined(options.cancelPreviousRequest)) {
			options.cancelPreviousRequest = true;
		}

		// If we are searching, don't update the active entryid
		if (!this.hasSearch) {
			if(!Ext.isEmpty(options.folder)) {
				// If a folder was provided in the options, we apply the folder
				this.setFolder(options.folder);
			} else if(Ext.isDefined(options.params.entryid) && Ext.isDefined(options.params.store_entryid)){
				// If the entryid was provided in the parameters we apply the params
				this.setEntryId(options.params.entryid, false);
				this.setStoreEntryId(options.params.store_entryid, false);
			}
		}

		if(!Ext.isEmpty(options.folder)) {
			Ext.applyIf(options.params, {
				id: options.folder[0].get('entryid')
			});
		}

		this.rootID = options.params.id;

		// Override the given entryid and store entryid.
		Ext.apply(options.params, {
			entryid : this.hasSearch ? this.searchFolderEntryId : this.entryId,
			store_entryid : this.storeEntryId
		});

		/*
		 * these options can be passed in arguments, or it can be set by setter methods of
		 * {@link Zarafa.core.data.ListModuleStore ListModuleStore}, like {@link #setRestriction}
		 * and {@link #setActionType}, advantage of using setter methods would be that
		 * all consecutive requestswill use that options if its not passed in arguments.
		 * but load method doesn't store these options automatically (like in case of entryids), so
		 * you have to call setter methods to actually set these options.
		 */
		Ext.applyIf(options, {
			actionType : this.actionType
		});

		Ext.applyIf(options.params, {
			restriction : this.restriction
		});

		if(this.restriction.search) {
			options.params.restriction = Ext.apply(options.params.restriction || {}, {
				search : this.restriction.search
			});
		}

		// search specific parameters
		if (options.actionType == Zarafa.core.Actions['search']) {
			/*
			 * below parameters are required for search so if its not passed in arguments
			 * then we have to add its default values
			 */
			Ext.applyIf(options.params, {
				use_searchfolder : this.useSearchFolder,
				subfolders : this.subfolders
			});
		}

		// remove options that are not needed, although sending it doesn't hurt
		if (options.actionType == Zarafa.core.Actions['updatesearch'] ||
			options.actionType == Zarafa.core.Actions['stopsearch']) {
			delete options.params.restriction;
		}

		return Zarafa.core.data.ListModuleStore.superclass.load.call(this, options);
	},

	/**
	 * Eventhandler that handles the beforeload event of the store.
	 * It will switch the viewmode if necessary.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store
	 * @param {Zarafa.plugins.files.data.FilesRecord} records
	 * @param {Object} options
	 */
	onLoad: function (store, records, options)
	{
		var path = options.params.id;
		var currViewPanel = Zarafa.plugins.files.data.ComponentBox.getItemsView();
		var toolbar = Zarafa.plugins.files.data.ComponentBox.getViewPanelToolbar();
		var previewPanel = Zarafa.plugins.files.data.ComponentBox.getPreviewPanel();
		var mainPanel = container.getMainPanel();

		//disable toolbar buttons
		toolbar.disable();
		if (!Ext.isDefined(path) || path === "#R#" || path === "") {
			// switch to the account overview!
			Zarafa.plugins.files.data.ComponentBox.getViewPanel().switchView('files-accountview');

			// remove all selections as we are in the root folder
			Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.unselectAllNavPanels();

			previewPanel.topToolbar.disable();
			mainPanel.filesSwitchViewButton.disable();

		} else {

			// enable toolbar buttons
			toolbar.enableButtons();

			if (currViewPanel instanceof Zarafa.plugins.files.ui.FilesRecordAccountView) {
				mainPanel.filesSwitchViewButton.enable();

				switch (Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentView()) {
					case Zarafa.plugins.files.data.Views.LIST:
						Zarafa.plugins.files.data.ComponentBox.getViewPanel().switchView('files-gridview');
						break;
					case Zarafa.plugins.files.data.Views.ICON:
						Zarafa.plugins.files.data.ComponentBox.getViewPanel().switchView('files-iconview');
						break;
				}
			}
		}
	},

	/**
	 * Eventhandler that handles the exception event of the store.
	 *
	 * @param proxy
	 * @param type
	 * @param action
	 * @param options
	 * @param response
	 * @param arg
	 */
	onLoadException: function (proxy, type, action, options, response, arg) {
		// handle unauthorized messages of plugins that need fronten authorization (dropbox, google,...)
		if(response.error && response.error.info.code) {
			if(parseInt(response.error.info.code) == 401) {
				// recall the auth procedure

				// first we need to get the account
				var failedID = options.params.id;
				var accID = Zarafa.plugins.files.data.Utils.File.getAccountId(failedID);

				var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

				// look up the account
				var account = accStore.getById(accID);

				if (Ext.isDefined(account) && account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.OAUTH)) {
					account.renewOauthToken();
				}
			}
		}
	},

	/**
	 * Reloads the Record cache from the configured Proxy. See the superclass {@link Zarafa.core.data.ListModuleStore#reload documentation}
	 * for more details.
	 * During reload we add an extra option into the {@link #load} argument which marks the action as a reload
	 * action.
	 *
	 * @param {Object} options
	 */
	reload: function (options) {
		var currentPath = this.getPath();

		// set the reload flag - then the backend will reload the cache!
		options = Ext.applyIf(options || {}, {
			params : {
				reload: true,
				id: currentPath
			},
			reload: true
		});

		Zarafa.plugins.files.data.FilesRecordStore.superclass.reload.call(this, options);

		// reload the navigator tree too
		if(!Ext.isDefined(options.noNavBar) || !options.noNavBar) {
			var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(currentPath);
			var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
			if(navPanel) {
				navPanel.refreshNode(currentPath);
			}
		}
	},

	/**
	 * Returns the current root directory.
	 *
	 * @return {String} The current root directory.
	 */
	getPath: function () {
		return this.rootID;
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.NavigatorTreeLoader
 * @extends Ext.tree.TreeLoader
 *
 * Files directory loader. Extends Ext treeloader to use Kopano
 * specific requests.
 */
Zarafa.plugins.files.data.NavigatorTreeLoader = Ext.extend(Ext.tree.TreeLoader, {

	/**
	 * @cfg {Boolean} If this flag is true, file records will also be loaded
	 * and visible.
	 */
	loadFiles: false,

	/**
	 * @cfg {Boolean} True to reload the cache.
	 */
	reload: false,


	/**
	 * @cfg {array|String} array of account ids or a single account id that should be loaded.
	 */
	accountFilter: null,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.reload)) {
			this.reload = config.reload;
		}

		if (Ext.isDefined(config.loadfiles)) {
			this.loadFiles = config.loadfiles;
		}

		if (Ext.isDefined(config.accountFilter)) {
			this.accountFilter = config.accountFilter;
		}

		Ext.applyIf(config, {
			preloadChildren: true,
			directFn       : this.loadFolder.createDelegate(this),
			listeners      : {
				loadexception: this.loadException
			}
		});

		Zarafa.plugins.files.data.NavigatorTreeLoader.superclass.constructor.call(this, config);
	},

	/**
	 * Will do single request to files module with provided nodeId and
	 * in case of success will load the content of this node.
	 *
	 * @param {Number} nodeId The id of node which content need to be loaded
	 * @param {Function} callback The function which need to be called after response received
	 */
	loadFolder: function (nodeId, callback) {
		var responseHandler = new Zarafa.plugins.files.data.ResponseHandler({
			successCallback: this.loadSuccess.createDelegate(this, [callback], true),
			failureCallback: this.loadFailure.createDelegate(this, [callback], true),
			nodeId         : nodeId
		});

		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'getfilestree',
			{
				id           : nodeId,
				loadfiles    : this.loadFiles,
				reload       : this.reload,
				accountFilter: this.accountFilter
			},
			responseHandler
		);
	},

	/**
	 * This method gets called after a successful response from the server.
	 * It will then call the callback method.
	 *
	 * @param items
	 * @param response
	 * @param callback
	 */
	loadSuccess: function (items, response, callback) {
		callback(items, response);
	},

	/**
	 * This method gets called after a failed response from the server.
	 * It will display a messagebox and then call the callback method.
	 *
	 * @param items
	 * @param response
	 * @param callback
	 */
	loadFailure: function (items, response, callback) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : dgettext('plugin_files', 'Error'),
			msg    : response.error,
			icon   : Zarafa.common.dialogs.MessageBox.ERROR,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});

		callback(undefined, {status: true, items: undefined});
	},

	/**
	 * This method gets called if a loading exception occurs.
	 * It will diplay a warning message to the user.
	 *
	 * @param tl
	 * @param node
	 * @param response
	 */
	loadException: function (tl, node, response) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : dgettext('plugin_files', 'Loading failed'),
			msg    : response.error,
			icon   : Zarafa.common.dialogs.MessageBox.ERROR,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});
	},

	/**
	 * Update the reload flag.
	 *
	 * @param reload
	 */
	setReload: function (reload) {
		this.reload = reload;
	}
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ResponseHandler
 * @extends Zarafa.core.data.AbstractResponseHandler
 * @xtype filesplugin.responsehandler
 *
 * Files plugin specific response handler.
 */
Zarafa.plugins.files.data.ResponseHandler = Ext.extend(Zarafa.core.data.AbstractResponseHandler, {

	/**
	 * @cgf {String} The id of the opened node in fuile tree recieved from the Files
	 */
	nodeId: undefined,

	/**
	 * @cfg {Function} successCallback The function which
	 * will be called after success request.
	 */
	successCallback: null,

	/**
	 * @cfg {Function} failureCallback The function which
	 * will be called after a failed request.
	 */
	failureCallback: null,

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetbackends: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetquota: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetversion: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetfilestree: function (response) {
		this.successCallback(response.items, response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doCheckifexists: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doDownloadtotmp: function (response) {
		this.successCallback(response.items, response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doCreatedir: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doRename: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doDelete: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doUploadtobackend: function (response) {
		this.successCallback(response);
	},

	/**
	 * In case exception happened on server, server will return
	 * exception response with the code of exception.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doError: function (response) {
		if (response.error) {
			Zarafa.common.dialogs.MessageBox.show({
				title  : dgettext('plugin_files', 'Error'),
				msg    : response.error.info.original_message,
				icon   : Zarafa.common.dialogs.MessageBox.ERROR,
				buttons: Zarafa.common.dialogs.MessageBox.OK
			});
		} else {
			Zarafa.common.dialogs.MessageBox.show({
				title  : dgettext('plugin_files', 'Error'),
				msg    : response.info.original_message,
				icon   : Zarafa.common.dialogs.MessageBox.ERROR,
				buttons: Zarafa.common.dialogs.MessageBox.OK
			});
		}
	}
});

Ext.reg('filesplugin.responsehandler', Zarafa.plugins.files.data.ResponseHandler);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.Utils
 * @singleton
 *
 * This class contains some static helper methods.
 */
Zarafa.plugins.files.data.Utils = {

	/**
	 * Base64 methods.
	 */
	Base64: {

		_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

		/**
		 * Encode the given string to base64.
		 *
		 * @param {String} input
		 * @return {String}
		 */
		encode: function (input) {
			var output = "";
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
			var i = 0;

			input = this._utf8_encode(input);

			while (i < input.length) {

				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output +
				this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
				this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

			}

			return output;
		},

		/**
		 * Decode the given base64 encoded string.
		 *
		 * @param {String} input
		 * @return {String}
		 */
		decode: function (input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			while (i < input.length) {

				enc1 = this._keyStr.indexOf(input.charAt(i++));
				enc2 = this._keyStr.indexOf(input.charAt(i++));
				enc3 = this._keyStr.indexOf(input.charAt(i++));
				enc4 = this._keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}

			}

			output = this._utf8_decode(output);

			return output;

		},

		/**
		 * Decodes the given base64 encoded utf-8 string.
		 *
		 * @param {String} input
		 * @return {String}
		 * @private
		 */
		_utf8_decode: function (utftext) {
			var string = "";
			var i = 0;
			var c = 0
			var c1 = 0;
			var c2 = 0;
			var c3 = 0;

			while (i < utftext.length) {

				c = utftext.charCodeAt(i);

				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if ((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i + 1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i + 1);
					c3 = utftext.charCodeAt(i + 2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}

			}

			return string;
		},

		/**
		 * Encode the given string to base64 in utf-8.
		 *
		 * @param {String} input
		 * @return {String}
		 * @private
		 */
		_utf8_encode: function (string) {
			string = string.replace(/\r\n/g, "\n");
			var utftext = "";

			for (var n = 0; n < string.length; n++) {

				var c = string.charCodeAt(n);

				if (c < 128) {
					utftext += String.fromCharCode(c);
				} else if ((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}

			}

			return utftext;
		}
	},

	/**
	 * Rendering methods
	 */
	Renderer: {

		/**
		 * Typerenderer for folders or files.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		typeRenderer: function (value, p, record) {
			switch (value) {
				case Zarafa.plugins.files.data.FileTypes.FOLDER:
					p.css = "zarafa-files-listview-icon " + Zarafa.plugins.files.data.Utils.File.getIconClass("folder", "16");
					break;
				case Zarafa.plugins.files.data.FileTypes.FILE:
					p.css = "zarafa-files-listview-icon " + Zarafa.plugins.files.data.Utils.File.getIconClass(record.get('filename'), "16");
					break;
				default :
					break;
			}

			p.css += ' zarafa-grid-empty-cell';

			return '';
		},

		/**
		 * Sharedrenderer for folders or files.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		sharedRenderer: function (value, p, record) {
			if (value) {
				p.css = "zarafa-files-listview-icon files_icon_16_share";
			}

			p.css += ' zarafa-grid-empty-cell';

			return '';
		},

		/**
		 * Renders timestamps.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		datetimeRenderer: function (value, p, record) {
			p.css = 'mail_date';

			value = new Date(value);
			return Ext.isDate(value) ? value.format(dgettext('plugin_files', 'l d/m/Y G:i')) : dgettext('plugin_files', 'Never');
		}
	},

	/**
	 * Formatting methods
	 */
	Format: {

		/**
		 * Simple format for a file size (xxx bytes, xxx KB, xxx MB, xxx GB).
		 *
		 * @param {Number/String} size The numeric value to format
		 * @return {String} The formatted file size
		 */
		fileSize: function (size) {
			if (size === -1 || size === "none") {
				return "";
			} else {
				if (size < 1024) {
					return size + " bytes";
				} else if (size < 1048576) {
					return (Math.round(((size * 10) / 1024)) / 10) + " KB";
				} else if (size < 1073741824) {
					return (Math.round(((size * 10) / 1048576)) / 10) + " MB";
				} else {
					return (Math.round(((size * 10) / 1073741824)) / 10) + " GB";
				}
			}
		},

		/**
		 * Simple format for a file size (xxx KB, xxx MB, xxx GB, xxx TB).
		 * It will display bytes in KB.
		 *
		 * @param {Number/String} size The numeric value to format
		 * @return {String} The formatted file size
		 */
		fileSizeList: function (size) {
			if (size === -1 || size === "none") {
				return "";
			} else {
				if (size < 1024) {
					if (size <= 100 && size != 0) {
						return "0.1 &nbsp;KB";
					} else {
						return (Math.round(((size * 10) / 1024)) / 10) + " &nbsp;KB";
					}
				} else if (size < 1048576) {
					return (Math.round(((size * 10) / 1024)) / 10) + " &nbsp;KB";
				} else if (size < 1073741824) {
					return (Math.round(((size * 10) / 1048576)) / 10) + " &nbsp;MB";
				} else if (size < 1099511627776) {
					return (Math.round(((size * 10) / 1073741824)) / 10) + " &nbsp;GB";
				} else {
					return (Math.round(((size * 10) / 1099511627776)) / 10) + " &nbsp;TB";
				}
			}
		},

		/**
		 * This functions truncates a string that is longer then the given length and appends
		 * three dots to the end of the truncated string.
		 *
		 * @param {String} string
		 * @param {Number} length
		 * @return {string}
		 */
		truncate: function (string, length) {
			return string.length > length ? string.substr(0, length - 1) + '&hellip;' : string;
		}
	},

	/**
	 * File/Path methods
	 */
	File: {

		/**
		 * Get the extension from the filename.
		 *
		 * @param {String} filename
		 * @return {String} Extension string without dot
		 */
		getExtension: function (filename) {
			var i = filename.lastIndexOf('.');
			return (i < 0) ? '' : filename.substr(i + 1);
		},

		/**
		 * Get the icon class for the filetype.
		 *
		 * @param {String} filename
		 * @param {String} size Iconsize without px
		 * @return {String} Icon class
		 */
		getIconClass: function (filename, size) {
			if (!Ext.isDefined(size)) {
				size = "48";
			}
			var existingIcons = ["aac", "ai", "aiff", "apk", "avi", "bmp", "c", "cpp", "css", "csv", "dat", "dmg",
				"doc", "docx", "dotx", "dwg", "dxf", "eps", "exe", "flv", "gif", "gz", "h", "hpp", "html", "ics",
				"iso", "java", "jpg", "js", "key", "less", "mid", "mp3", "mp4", "mpg", "odf", "ods",
				"odt", "otp", "ots", "ott", "pdf", "php", "png", "ppt", "psd", "py", "qt", "rar",
				"rb", "rtf", "sass", "sql", "tga", "tgz", "tiff", "txt", "wav", "xls", "xlsx", "xml",
				"yml", "zip"];

			if (filename === "folder") {
				return "files" + size + "icon_folder";
			}

			var extension = this.getExtension(filename).toLowerCase();
			var exists = existingIcons.indexOf(extension);

			if (Ext.isEmpty(extension) || exists === -1) {
				return "files" + size + "icon_blank";
			}

			return "files" + size + "icon_" + extension;
		},

		/**
		 * Check if the filename (or foldername) includes unwanted characters.
		 *
		 * @param {String} filename
		 * @return {Boolean} true if filename is valid
		 */
		isValidFilename: function (filename) {

			// empty filenames are not allowed
			if (Ext.isEmpty(filename)) {
				return false;
			}

			// filename must not contain a slash
			if (filename.indexOf("/") !== -1) {
				return false;
			}

			// this symbols are not allowed in owncloud
			if (filename.indexOf("\\") !== -1
				|| filename.indexOf("<") !== -1
				|| filename.indexOf(">") !== -1
				|| filename.indexOf(":") !== -1
				|| filename.indexOf("'") !== -1
				|| filename.indexOf("|") !== -1
				|| filename.indexOf("?") !== -1
				|| filename.indexOf("*") !== -1
			) {
				return false;
			}

			return true;
		},

		/**
		 * Parse the account ID from the complete file ID.
		 * File ID might be something like "#R#89621e82/folder/file.png"
		 * so the account ID is "89621e82" then.
		 *
		 * @param fileid
		 * @return {string|null}
		 */
		getAccountId: function (fileid) {
			if (!Ext.isDefined(fileid)) {
				return null;
			}

			if(fileid.indexOf("/") === -1) {
				return null;
			}

			var startpos = 0;
			if (fileid.indexOf("#R#") == 0) {
				startpos = 3;
			}
			return fileid.substr(startpos, fileid.indexOf('/') - startpos);
		},

		/**
		 * Remove the account ID from the complete file ID.
		 * File ID might be something like "#R#89621e82/folder/file.png"
		 *
		 * @param fileid
		 * @return {string}
		 */
		stripAccountId: function (fileid) {
			if (!Ext.isDefined(fileid)) {
				return false;
			}

			if(fileid.indexOf("/") === -1) {
				return '#R#';
			}

			return fileid.substr(fileid.indexOf('/'));
		},

		/**
		 * Return the filename for the given path.
		 *
		 * @param path
		 * @returns {String}
		 */
		getFileName: function (path) {
			return path.replace(/^.*[\\\/]/, '');
		},

		/**
		 * Return the parent foldername for the given path.
		 *
		 * @param path
		 * @returns {String}
		 */
		getDirName: function (path) {
			return path.replace(/\\/g, '/').replace(/\/[^\/]*\/?$/, '');
		}
	},

	/**
	 * WebApp Core methods
	 */
	Core: {
		/**
		 * This will return the maximum upload size.
		 *
		 * @return {Number} Maximum upload size in bytes.
		 */
		getMaxUploadFilesize: function () {

			return container.getServerConfig().getMaxAttachmentSize(); // TODO: make it variable
		}
	},

	/**
	 * Validator methods
	 */
	Validator: {
		/**
		 * This filter should be applied to all action buttons. It will for example hide the button if
		 * a special functionality is not available.
		 *
		 * @param records
		 * @param singleSelectOnly
		 * @param fileOnly
		 * @param noRoot
		 * @returns {boolean}
		 */
		actionSelectionVisibilityFilter: function (records, singleSelectOnly, fileOnly, noRoot) {
			var visible = true;

			if(!Ext.isDefined(records) || Ext.isEmpty(records)) {
				return false;
			}

			if (singleSelectOnly) {
				if (!Ext.isDefined(records) || (Ext.isArray(records) && records.length != 1)) {
					visible = false;
				}
			}

			if (fileOnly) {
				for (var i = 0; i < records.length; i++) {
					var record = records[i];
					if (record.get('type') == Zarafa.plugins.files.data.FileTypes.FOLDER) {
						visible = false;
						break;
					}
				}
			}

			if (noRoot) {
				for (var i = 0; i < records.length; i++) {
					var record = records[i];
					if (record.get('filename') === "..") {
						visible = false;
						break;
					}
				}
			}

			return visible;
		}
	}
};
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ViewModes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different viewing modes of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.ViewModes = Zarafa.core.Enum.create({

	/**
	 * Don't show the preview panel
	 * @property
	 * @type Number
	 */
	NO_PREVIEW: 0,

	/**
	 * Show the preview panel to the right
	 * @property
	 * @type Number
	 */
	RIGHT_PREVIEW: 1,

	/**
	 * Show the preview panel in the bottom
	 * @property
	 * @type Number
	 */
	BOTTOM_PREVIEW: 2
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.Views
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different views of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.Views = Zarafa.core.Enum.create({

	/**
	 * View all files items from the selected folder(s) in the 'list' view.
	 *
	 * @property
	 * @type Number
	 */
	LIST: 0,

	/**
	 * View all files items from the selected folder(s) in the 'icon' view.
	 *
	 * @property
	 * @type Number
	 */
	ICON: 1
});
Ext.namespace('Zarafa.plugins.files.data.singleton');

/**
 * @class Zarafa.plugins.files.data.singleton.AccountStore
 * @extends Object
 * @singleton
 *
 * This singleton will hold a reference to the {@link Zarafa.plugins.files.data.AccountStore Accountstore}.
 */
Zarafa.plugins.files.data.singleton.AccountStore = Ext.extend(Object, {

	/**
	 * @property
	 * @type Zarafa.plugins.files.data.AccountStore
	 * @private
	 */
	store: undefined,

	/**
	 * Triggers a call to the backend to load version information.
	 */
	init: function () {
		this.store = new Zarafa.plugins.files.data.AccountStore();
		this.store.load();
	},

	/**
	 * Get instance of the {@link Zarafa.plugins.files.data.AccountStore Accountstore}
	 * @return {Zarafa.plugins.files.data.AccountStore}
	 */
	getStore: function () {
		return this.store;
	}
});

// Make it a Singleton
Zarafa.plugins.files.data.singleton.AccountStore = new Zarafa.plugins.files.data.singleton.AccountStore();
Ext.namespace('Zarafa.plugins.files.data.singleton');

/**
 * @class Zarafa.plugins.files.data.singleton.BackendController
 * @extends Object
 * @singleton
 *
 * This class offers basic access to backend values (name, type, ...).
 */
Zarafa.plugins.files.data.singleton.BackendController = Ext.extend(Object, {

	/**
	 * @property
	 * @type Object
	 * @private
	 */
	backendData: undefined,

	/**
	 * This method initializes the {@link Zarafa.plugins.files.data.singleton.BackendController backend controller}.
	 * It will load all data from the server.
	 */
	init: function () {
		var responseHandler = new Zarafa.plugins.files.data.ResponseHandler({
			successCallback: this.gotBackendValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getbackends',
			{
				plugin: "files"
			},
			responseHandler
		);
	},

	/**
	 * This method gets called if the initialization was successful.
	 *
	 * @param {Object} response
	 */
	gotBackendValues: function (response) {
		this.backendData = response;
	},

	/**
	 * This method will return a {@link Ext.data.ArrayStore store} that contains all available backend names.
	 *
	 * @return {Ext.data.ArrayStore} Store with all backend names.
	 */
	getBackendNameStore: function () {
		var dataFields = [];

		if (Ext.isDefined(this.backendData) && Ext.isDefined(this.backendData.backends)) {
			Ext.each(this.backendData.backends, function (backend) {
				var entry = [
					backend.name,
					backend.displayName,
					backend.description
				];

				dataFields.push(entry);
			});
		}

		var store = new Ext.data.ArrayStore({
			fields: ['backend', 'displayName', 'displayText'],
			data  : dataFields
		});

		return store;
	}
});

// Make it a singleton.
Zarafa.plugins.files.data.singleton.BackendController = new Zarafa.plugins.files.data.singleton.BackendController();
Ext.namespace('Zarafa.plugins.files.data.singleton');

/**
 * @class Zarafa.plugins.files.data.singleton.FilesRecordStoreManager
 * @extends Object
 * @singleton
 *
 * This singleton will hold a reference to {@link Zarafa.plugins.files.data.FilesRecordStore FilesRecordStore}.
 */
Zarafa.plugins.files.data.singleton.FilesRecordStoreManager = Ext.extend(Object, {

	/**
	 * @property
	 * @type Zarafa.plugins.files.data.FilesRecordStore[]
	 * @private
	 */
	stores: undefined,

	/**
	 * Get instance of the {@link Zarafa.plugins.files.data.FilesRecordStore FilesRecordStore}
	 *
	 * @param {Number} storeid the storeid to identify the store.
	 * 		If it is empty the default store (id = 0) will be returned.
	 *
	 * @return {Zarafa.plugins.files.data.FilesRecordStore}
	 */
	getStore: function (storeid) {
		if(Ext.isEmpty(storeid)) {
			storeid = 0;
		}

		if(!Ext.isDefined(this.stores)) {
			// Generate the store
			this.stores = [];

			this.stores[storeid] = new Zarafa.plugins.files.data.FilesRecordStore();

			return this.stores[storeid];
		} else if (Ext.isDefined(this.stores[storeid])) {
			return this.stores[storeid];
		} else {
			// Generate a new store for the given id
			this.stores[storeid] = new Zarafa.plugins.files.data.FilesRecordStore();

			return this.stores[storeid];
		}
	}
});

// Make it a Singleton
Zarafa.plugins.files.data.singleton.FilesRecordStoreManager = new Zarafa.plugins.files.data.singleton.FilesRecordStoreManager();
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.files.settings.SettingsAccountsWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype filesplugin.settingsaccountswidget
 *
 * The {@link Zarafa.settings.ui.SettingsWidget widget} for configuring
 * the general files options in the {@link Zarafa.files.settings.SettingsFilesCategory files category}.
 */
Zarafa.plugins.files.settings.SettingsAccountsWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title : dgettext('plugin_files', 'Manage Accounts'),
			xtype : 'filesplugin.settingsaccountswidget',
			height: 400,
			layout: {

				// override from SettingsWidget
				type: 'fit'
			},
			items : [{
				xtype: "filesplugin.accountpanel"
			}]
		});

		Zarafa.plugins.files.settings.SettingsAccountsWidget.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.settingsaccountswidget', Zarafa.plugins.files.settings.SettingsAccountsWidget);
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.plugins.files.settings.SettingsMainCategory
 * @extends Zarafa.settings.ui.SettingsCategory
 * @xtype filesplugin.settingsmaincategory
 *
 * The files category for users which will
 * allow the user to configure Files related settings
 */
Zarafa.plugins.files.settings.SettingsMainCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title        : dgettext('plugin_files', 'Files'),
			categoryIndex: 1,
			iconCls      : 'icon_files_category',
			items        : [{
				xtype: 'filesplugin.settingsaccountswidget'
			}, {
				xtype: 'filesplugin.settingsresetwidget'
			},
				container.populateInsertionPoint('context.settings.category.files', this)
			]
		});

		Zarafa.plugins.files.settings.SettingsMainCategory.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.settingsmaincategory', Zarafa.plugins.files.settings.SettingsMainCategory);
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.plugins.files.settings.SettingsResetWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype filesplugin.settingsresetwidget
 *
 * The Reset Settings widget
 */
Zarafa.plugins.files.settings.SettingsResetWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * The loadMask object which will be shown when reset request is being sent to the server.
	 * @property
	 * @type Zarafa.common.ui.LoadMask
	 */
	loadMask: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title : dgettext('plugin_files', 'Reset Files settings'),
			layout: 'form',
			items : [{
				xtype    : 'displayfield',
				hideLabel: true,
				value    : dgettext('plugin_files', 'Resets Files settings to their original defaults')
			}, {
				xtype  : 'button',
				text   : dgettext('plugin_files', 'Reset Files settings'),
				width  : 150,
				handler: this.onResetSettings,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.SettingsResetWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Event handler when the "Reset Files Settings" button was clicked.
	 * This will {@link Zarafa.settings.SettingsModel#reset reset} the
	 * {@link Zarafa.settings.data.SettingsDefaultValue values} of the settings.
	 * @private
	 */
	onResetSettings: function () {
		var message = dgettext('plugin_files', 'Your Files\'s settings will be restored to their default condition.')
			+ ' ' + dgettext('plugin_files', 'Are you sure you want to reset all Files settings and remove all accounts?');
		message += '<br/><br/>';
		message += dgettext('plugin_files', 'WebApp will automatically restart in order for these changes to take effect');
		message += '<br/>';

		Zarafa.common.dialogs.MessageBox.addCustomButtons({
			title       : dgettext('plugin_files', 'Reset Files settings'),
			msg         : message,
			icon        : Ext.MessageBox.QUESTION,
			fn          : this.resetDefaultSettings,
			customButton: [{
				text: dgettext('plugin_files', 'Reset'),
				name: 'reset'
			}, {
				text: dgettext('plugin_files', 'Cancel'),
				name: 'cancel'
			}],
			scope       : this
		});

	},

	/**
	 * Event handler for {@link #onResetSettings}. This will check if the user
	 * wishes to reset the default settings or not.
	 * @param {String} button The button which user pressed.
	 * @private
	 */
	resetDefaultSettings: function (button) {
		if (button === 'reset') {
			var settingsModel = container.getSettingsModel();
			var accounts = settingsModel.getSettingsObject('zarafa/v1/plugins/files/accounts');
			for (var account in accounts) {
				if (accounts[account].cannot_change !== true) {
					settingsModel.reset('zarafa/v1/plugins/files/accounts/' + account);
				}
			}
			settingsModel.reset('zarafa/v1/contexts/files');
			settingsModel.save();

			this.loadMask = new Zarafa.common.ui.LoadMask(Ext.getBody(), {
				msg: '<b>' + dgettext('plugin_files', 'Webapp is reloading, Please wait.') + '</b>'
			});

			this.loadMask.show();

			this.mon(settingsModel, 'save', this.onSettingsSave, this);
			this.mon(settingsModel, 'exception', this.onSettingsException, this);
		}

	},

	/**
	 * Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#save save}
	 * event to indicate the settings were successfully saved and it will forcefully realod the webapp.
	 * @param {Zarafa.settings.SettingsModel} model The model which fired the event.
	 * @param {Object} parameters The key-value object containing the action and the corresponding
	 * settings which were saved to the server.
	 * @private
	 */
	onSettingsSave: function (model, parameters) {
		if (parameters.action === Zarafa.core.Actions['reset']) {
			this.mun(model, 'save', this.onSettingsSave, this);
			this.mun(model, 'exception', this.onSettingsException, this);
			Zarafa.core.Util.reloadWebapp();
		}
	},

	/**
	 * Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#exception exception}
	 * event to indicate the settings were not successfully saved.
	 * @param {Zarafa.settings.SettingsModel} model The settings model which fired the event
	 * @param {String} type The value of this parameter will be either 'response' or 'remote'.
	 * @param {String} action Name of the action (see {@link Ext.data.Api#actions}).
	 * @param {Object} options The object containing a 'path' and 'value' field indicating
	 * respectively the Setting and corresponding value for the setting which was being saved.
	 * @param {Object} response The response object as received from the PHP-side
	 * @private
	 */
	onSettingsException: function (model, type, action, options, response) {
		if (options.action === Zarafa.core.Actions['reset']) {
			this.loadMask.hide();

			this.mun(model, 'save', this.onSettingsSave, this);
			this.mun(model, 'exception', this.onSettingsException, this);
		}
	}
});

Ext.reg('filesplugin.settingsresetwidget', Zarafa.plugins.files.settings.SettingsResetWidget);Ext.namespace('Zarafa.plugins.files.settings.data');

/**
 * @class Zarafa.plugins.files.settings.data.AccountRenderUtil
 * @singleton
 *
 * This class offers some basic utils for rendering account specific values.
 */
Zarafa.plugins.files.settings.data.AccountRenderUtil = {

	/**
	 * Renderer for the status column of the accountgrid
	 * @param {Object} value The data value for the cell.
	 * @param {Object} p An object with metadata
	 * @param {Ext.data.Record} record The {Ext.data.Record} from which the data was extracted.
	 * @return {String} The formatted string
	 */
	statusRenderer: function (value, p, record) {

		switch (value) {
			case Zarafa.plugins.files.data.AccountRecordStatus.OK:
				p.css = "zarafa-files-listview-icon zarafa-files-account-ok";
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.NEW:
				p.css = "zarafa-files-listview-icon zarafa-files-account-new";
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.ERROR:
				p.css = "zarafa-files-listview-icon zarafa-files-account-error";
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.UNKNOWN:
				p.css = "zarafa-files-listview-icon zarafa-files-account-unknown";
				break;
			default :
				break;
		}

		// add extra css class for empty cell
		p.css += ' zarafa-grid-empty-cell';
		p.attr = 'ext:qtip="' + record.get("status_description") + '"';

		return '';
	},

	/**
	 * Renderer for the feature column of the accountgrid
	 * @param value
	 * @param metadata
	 * @param record
	 * @param rowIndex
	 * @param colIndex
	 * @param store
	 * @param feature
	 * @returns {string}
	 */
	featureRenderer: function (value, metadata, record, rowIndex, colIndex, store, feature) {
		var availableFeatures = record.get('backend_features');

		// hide this feature if account does not support it
		if (!Ext.isDefined(availableFeatures) || availableFeatures === null || !Zarafa.plugins.files.settings.data.AccountRenderUtil.arrayContains(availableFeatures, feature)) {
			return 'x-hide-display';
		}
		return 'zarafa-files-feature-spacer';
	},

	/**
	 * Renderer for the backend column of the accountgrid
	 * @param value
	 * @param metadata
	 * @param record
	 * @param rowIndex
	 * @param colIndex
	 * @param store
	 * @param feature
	 * @returns {string}
	 */
	backendRenderer: function (value, metadata, record, rowIndex, colIndex, store, feature) {
		var backendNameStore = Zarafa.plugins.files.data.singleton.BackendController.getBackendNameStore();
		var backendNameObj = backendNameStore.getAt(backendNameStore.findExact("backend", value));

		return '<span class="icon_16_' + value + ' files_backend_selector">&nbsp;</span>' + backendNameObj.get("displayName");
	},

	/**
	 * Check if a array contains the needle
	 * @param array
	 * @param needle
	 * @returns {boolean}
	 */
	arrayContains: function (array, needle) {
		var i = array.length;
		while (i--) {
			if (array[i] === needle) {
				return true;
			}
		}
		return false;
	}
};Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountEditContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.accounteditcontentpanel
 */
Zarafa.plugins.files.settings.ui.AccountEditContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.accounteditcontentpanel',

			layout: 'fit',
			modal: true,
			width: 400,
			height: 250,
			stateful: false,
			title: dgettext('plugin_files', 'Edit Account'),
			items: [{
				xtype: 'filesplugin.accounteditpanel',
				item: config.item
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountEditContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.accounteditcontentpanel', Zarafa.plugins.files.settings.ui.AccountEditContentPanel);Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountEditPanel
 * @extends Ext.Panel
 * @xtype filesplugin.accounteditpanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.AccountEditContentPanel AccountEditContentPanel}.
 */
Zarafa.plugins.files.settings.ui.AccountEditPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Object} The current loaded account record.
	 */
	currentItem: undefined,

	/**
	 * @constructor
	 * @param config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.currentItem = config.item;
		}

		Ext.applyIf(config, {

			xtype: 'filesplugin.accounteditpanel',
			layout: "anchor",
			autoScroll: true,
			border: false,
			items: this.createPanelItems(config),
			buttons: [{
				text: dgettext('plugin_files', 'Save'),
				ref: "../saveBtn",
				cls: "zarafa-action",
				disabled: true,
				handler: this.doSave,
				scope: this
			}, {
				text: dgettext('plugin_files', 'Cancel'),
				handler: this.doClose,
				scope: this
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountEditPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * If there is no existing record, this function will create a new @see Zarafa.plugins.files.data.AccountRecord
	 * and fill it with the form values.
	 * Afterwards the record will be saved to the server-backend.
	 */
	doSave: function () {
		var store = this.dialog.store;
		var accountName = this.dialog.accName;
		var accountBackend = this.dialog.accBackend;
		var metaFormItems = this.dialog.metaForm.form.items.items;
		var formValid = true;

		// build the configuration object from the formfields
		var backendConfig = {};

		// also do some sanity checks on the values
		Ext.each(metaFormItems, function (formRecord) {
			backendConfig[formRecord.getName()] = formRecord.getValue();

			// check if record is valid
			if (!formRecord.isValid()) {
				formValid = false;
				return false;
			}
		});

		if (formValid) {
			if (!this.currentItem) {   // create new record if none exists yet
				this.currentItem = new store.recordType({
					id            : -1,
					name          : accountName.getValue(),
					status        : Zarafa.plugins.files.data.AccountRecordStatus.NEW,
					backend       : accountBackend.getValue(),
					backend_config: backendConfig
				});

				store.add(this.currentItem);
			} else {   // edit the existing record
				this.currentItem.beginEdit();
				this.currentItem.set('name', accountName.getValue());
				this.currentItem.set('status', Zarafa.plugins.files.data.AccountRecordStatus.NEW);
				this.currentItem.set('backend', accountBackend.getValue());
				this.currentItem.set('backend_config', backendConfig);
				this.currentItem.endEdit();
			}

			// close the dialog after success.
			this.doClose();
		} else {

			// TODO: print error
		}
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.AccountEditPanel AccountEditPanel}.
	 *
	 * @param {Object} config
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function (config) {

		// default values
		var name = "";
		var backend = "";
		var initMetaForm = false;
		var formConfigUrl;

		if (Ext.isDefined(config.item)) {// set defaultvalues if available
			name = config.item.get("name");
			backend = config.item.get("backend");
			initMetaForm = true;
			formConfigUrl = Ext.urlAppend(container.getBaseURL(), 'load=custom&name=form&backend=' + encodeURI(backend));
		}

		return [{
			xtype         : 'fieldset',
			checkboxToggle: false,
			title         : dgettext('plugin_files', 'Account Information'),
			defaultType   : 'textfield',
			ref           : 'accInfo',
			collapsed     : false,
			items         : [{
				xtype       : 'panel',
				layout      : 'form',
				border : false,
				flex : 1,
				defaults : {
					anchor :'100%',
					style : 'margin-bottom: 10px;'
				},
				defaultType: 'textfield',
				items      : [{
					fieldLabel: dgettext('plugin_files', 'Account name'),
					labelAlign: 'top',
					ref       : '../../../accName',
					value     : name,
					name      : "accountName"
				}, {
					xtype         : "combo",
					fieldLabel    : dgettext('plugin_files', 'Account type'),
					ref           : '../../../accBackend',
					store         : Zarafa.plugins.files.data.singleton.BackendController.getBackendNameStore(),
					valueField    : 'backend',
					value         : backend,
					displayField  : 'displayName',

					// Template for the dropdown menu.
					// Note the use of "x-combo-list-item" class,
					// this is required to make the items selectable.
					tpl           : '<tpl for="."><div ext:qtip="{displayText}" class="x-combo-list-item"><span class="icon_16_{backend} files_backend_selector">&nbsp;</span>{displayName}</div></tpl>',
					emptyText     : dgettext('plugin_files', 'Select backend...'),
					triggerAction : 'all',
					mode          : 'local',
					forceSelection: true,
					editable      : false,
					listeners     : {
						select: this.onBackendSelect,
						scope : this
					}
				}]
			}]
		}, {
			xtype         : 'fieldset',
			checkboxToggle: false,
			title         : dgettext('plugin_files', 'Account Configuration'),
			autoHeight    : true,
			defaultType   : 'textfield',
			ref           : 'metaInfo',
			collapsed     : false,
			items         : [{
				xtype    : 'metaform',
				autoInit : initMetaForm,
				method   : 'GET',
				flex : 1,
				defaults : {
					anchor :'100%',
					style : 'margin-bottom: 10px;'
				},
				ref      : '../../metaForm',
				url      : formConfigUrl,
				listeners: {
					actioncomplete: this.onMetaFormReady.createDelegate(this)
				}
			}]
		}];
	},

	/**
	 * Called after the user select a backend from the dropdownlist.
	 * It will reinitialize the metaform.
	 *
	 * @param {Ext.form.ComboBox} combo
	 * @param {Ext.data.Record} record
	 * @param {Number} index
	 */
	onBackendSelect: function (combo, record, index) {
		var selectedBackend = record.data.backend;
		var metaForm = this.dialog.metaForm;
		var saveButton = this.saveBtn;
		var formConfigUrl = Ext.urlAppend(container.getBaseURL(), 'load=custom&name=form&backend=' + encodeURI(selectedBackend));

		// reinitialize metaform
		metaForm.url = formConfigUrl;
		metaForm.load();

		// enable the save button
		saveButton.enable();
	},

	/**
	 * Fired after meta data is processed and form fields are created.
	 */
	onMetaFormReady: function () {
		if (Ext.isDefined(this.item)) {
			var saveButton = this.saveBtn;

			// initialize metaform values
			this.dialog.metaForm.bindData(this.item.data.backend_config);

			// enable the save button
			saveButton.setText(dgettext('files_plugin', 'Update'));
			saveButton.enable();
		}

		// FIXME: this is a workaround for IE 9, IE 10 and IE 11
		// chrome and ff will work without this re-layouting
		this.dialog.metaForm.on('afterlayout', function () {
			this.dialog.metaForm.doLayout();
			var win = Ext.WindowMgr.getActive();
			win.setHeight(this.accInfo.getHeight() + this.metaInfo.getHeight() + 90);
		}, this, {single: true});
	}
});

Ext.reg('filesplugin.accounteditpanel', Zarafa.plugins.files.settings.ui.AccountEditPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountGrid
 * @extends Ext.grid.GridPanel
 * @xtype filesplugin.accountgrid
 *
 * The main gridpanel for our account list.
 */
Zarafa.plugins.files.settings.ui.AccountGrid = Ext.extend(Zarafa.common.ui.grid.GridPanel, {

	/**
	 * @cfg {Object} The account store.
	 */
	store: null,

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		this.store = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		Ext.applyIf(config, {
			xtype       : 'filesplugin.accountgrid',
			ref         : 'accountgrid',
			store       : this.store,
			border      : false,
			baseCls     : 'accountGrid',
			enableHdMenu: false,
			loadMask    : this.initLoadMask(),
			viewConfig  : this.initViewConfig(),
			sm          : this.initSelectionModel(),
			cm          : this.initColumnModel(),
			listeners   : {
				rowdblclick: this.onRowDblClick,
				scope      : this
			},
			tbar        : [{
				iconCls: 'filesplugin_icon_add',
				text   : dgettext('plugin_files', 'Add Account'),
				ref    : '../addAccountBtn',
				handler: this.onAccountAdd
			}, {
				iconCls : 'filesplugin_icon_delete',
				text    : dgettext('plugin_files', 'Remove Account'),
				ref     : '../removeAccountBtn',
				disabled: true,
				scope   : this,
				handler : this.onAccountRemove
			}, {
				xtype : 'spacer',
				width : 10
			}, {
				xtype : 'button',
				iconCls : 'zarafa-rules-sequence-up',
				disabled : true,
				ref : '../upButton',
				handler : this.onAccountSequenceUp,
				scope : this,
				width : 20
			}, {
				xtype : 'spacer',
				width : 10
			}, {
				xtype : 'button',
				iconCls : 'zarafa-rules-sequence-down',
				disabled : true,
				ref : '../downButton',
				handler : this.onAccountSequenceDown,
				scope : this,
				width : 20
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountGrid.superclass.constructor.call(this, config);
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.loadMask} field.
	 *
	 * @return {Ext.LoadMask} The configuration object for {@link Ext.LoadMask}
	 * @private
	 */
	initLoadMask: function () {
		return {
			msg: dgettext('plugin_files', 'Loading accounts') + '...'
		};
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel#viewConfig} field.
	 *
	 * @return {Ext.grid.GridView} The configuration object for {@link Ext.grid.GridView}
	 * @private
	 */
	initViewConfig: function () {
		/*
		 * enableRowBody is used for enabling the rendering of
		 * the second row in the compact view model. The actual
		 * rendering is done in the function getRowClass.
		 *
		 * NOTE: Even though we default to the extended view,
		 * enableRowBody must be enabled here. We disable it
		 * later in onContextViewModeChange(). If we set false
		 * here, and enable it later then the row body will never
		 * be rendered. So disabling after initializing the data
		 * with the rowBody works, but the opposite will not.
		 */

		return {
			enableRowBody: false,
			forceFit     : true,
			emptyText    : '<div class=\'emptytext\'>' + dgettext('plugin_files', 'No account created!') + '</div>',
			getRowClass: function(record) {
				if (record.get('status') !== 'ok') {
					return 'files-faulty-account';
				}
			}
		};
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.sm SelectionModel} field.
	 *
	 * @return {Ext.grid.RowSelectionModel} The subclass of {@link Ext.grid.AbstractSelectionModel}
	 * @private
	 */
	initSelectionModel: function () {
		return new Ext.grid.RowSelectionModel({
			singleSelect: true,
			listeners   : {
				selectionchange: this.onRowSelected
			}
		});
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.cm ColumnModel} field.
	 *
	 * @return {Ext.grid.ColumnModel} The {@link Ext.grid.ColumnModel} for this grid
	 * @private
	 */
	initColumnModel: function () {
		return new Zarafa.plugins.files.settings.ui.AccountGridColumnModel();
	},

	/**
	 * Function is called if a row in the grid gets selected.
	 *
	 * @param selectionModel
	 */
	onRowSelected: function (selectionModel) {
		if (selectionModel.getCount() < 1) {
			return;
		}
		var remButton = this.grid.removeAccountBtn;
		var isAdministrativeAccount = selectionModel.getSelections()[0].get("cannot_change");
		remButton.setDisabled(isAdministrativeAccount);

		this.grid.upButton.setDisabled(!selectionModel.hasPrevious());
		this.grid.downButton.setDisabled(!selectionModel.hasNext());
	},

	/**
	 * Function is called if a row in the grid gets double clicked.
	 *
	 * @param grid
	 * @param rowIndex
	 */
	onRowDblClick: function (grid, rowIndex) {
		if (this.getSelectionModel().getSelections()[0].get("cannot_change")) {
			return;
		}
		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['filesplugin.accountedit'], undefined, {
			store  : grid.getStore(),
			item   : grid.getStore().getAt(rowIndex),
			manager: Ext.WindowMgr
		});
	},

	/**
	 * Clickhandler for the "add account" button.
	 *
	 * @param button
	 * @param event
	 */
	onAccountAdd: function (button, event) {
		var grid = button.refOwner;

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['filesplugin.accountedit'], undefined, {
			store  : grid.getStore(),
			manager: Ext.WindowMgr
		});
	},

	/**
	 * Clickhandler for the "remove account" button.
	 */
	onAccountRemove: function () {
		var selections = this.getSelectionModel().getSelections();

		// Warn user before deleting the account!
		if (Ext.isDefined(selections[0])) { // as we have single select, remove the first item
			Ext.MessageBox.confirm(
				dgettext('plugin_files', 'Confirm deletion'),
				String.format(dgettext('plugin_files', 'Do you really want to delete the account "{0}"?'), selections[0].get("name")),
				this.doRemove.createDelegate(this, [selections[0]], true),
				this
			);
		}
	},

	/**
	 * Actually removes the given account from the backend.
	 *
	 * @param {String} button
	 * @param {String} value Unused
	 * @param {Object} options Unused
	 * @param {AccountRecord} account
	 */
	doRemove: function (button, value, options, account) {
		if (button === "yes") {
			this.getStore().remove(account);
		}
	},

	/**
	 * Handler function will be called when user clicks on 'Up' button
	 * This will determine which accounts to swap and call {@link #swapAccounts}.
	 * @private
	 */
	onAccountSequenceUp : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var account = sm.getSelected();

		/*
		 * Start looking for the first sequence number which is lower then
		 * the current sequence number. Note that we want the account_sequence
		 * which is closest to the current account_sequence, hence the account:
		 *    account.get('account_sequence') > record.get('account_sequence') > swapAccount.get('account_sequence')
		 */
		var swapAccount;
		store.each(function(record) {
			if (account.get('account_sequence') > record.get('account_sequence')) {
				if (!swapAccount || record.get('account_sequence') > swapAccount.get('account_sequence')) {
					swapAccount = record;
				}
			}
		}, this);

		this.swapAccounts(account, swapAccount);
	},

	/**
	 * Handler function will be called when user clicks on 'Down' button
	 * This will determine which accounts to swap and call {@link #swapAccounts}.
	 * @private
	 */
	onAccountSequenceDown : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var account = sm.getSelected();

		/*
		 * Start looking for the first sequence number which is higher then
		 * the current sequence number. Note that we want the account_sequence
		 * which is closest to the current account_sequence, hence the account:
		 *    account.get('account_sequence') < record.get('account_sequence') < swapAccount.get('account_sequence')
		 */
		var swapAccount;
		store.each(function(record) {
			if (account.get('account_sequence') < record.get('account_sequence')) {
				if (!swapAccount || record.get('account_sequence') < swapAccount.get('account_sequence')) {
					swapAccount = record;
				}
			}
		}, this);

		this.swapAccounts(account, swapAccount);
	},

	/**
	 * Swap two accounts by changing the 'account_sequence' property
	 * for both accounts, and {@link Ext.data.Store#sort sort}
	 * the {@link #store}.
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 * @private
	 */
	swapAccounts : function(a, b)
	{
		var aSeq = parseInt(a.get('account_sequence'));
		var bSeq = parseInt(b.get('account_sequence'));

		// Disable UI buttons to prevent race conditions
		this.upButton.setDisabled(true);
		this.downButton.setDisabled(true);

		// Swap the 2 accounts
		this.store.suspendEvents(); // do not use the autoSave feature of the store
		a.set('account_sequence', bSeq);
		b.set('account_sequence', aSeq);
		this.store.resumeEvents();
		a.markDirty();
		b.markDirty();

		// store both accounts in one request
		this.store.save(this.store.getModifiedRecords());

		// Reapply the sorting, this will update the UI
		this.store.on('update', this.onAfterSequenceChanged.createDelegate(this, [a,b], true), null, {single: true});
	},

	/**
	 * Eventhandler, called after the store has been updated.
	 * This will reload the store to update the ordering.
	 *
	 * @param store
	 * @param record
	 * @param operation
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 */
	onAfterSequenceChanged : function(store, record, operation, a, b) {
		store.reload();

		// Update the UI when the store has been reloaded
		store.on('load', this.onAfterSequenceReload.createDelegate(this, [a,b], true), null, {single: true});
	},

	/**
	 * This updates the UI after the store has been reloaded.
	 *
	 * @param store
	 * @param record
	 * @param operation
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 */
	onAfterSequenceReload: function(store, record, operation, a, b) {
		var grid = this;

		 // Update the 'up'/'down' button
		var sm = grid.getSelectionModel();
		grid.upButton.setDisabled(!sm.hasPrevious());
		grid.downButton.setDisabled(!sm.hasNext());

		// fire the reorder event
		store.fireEvent('reorder', a, b);
	}
});

Ext.reg('filesplugin.accountgrid', Zarafa.plugins.files.settings.ui.AccountGrid);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountGridColumnModel
 * @extends Zarafa.common.ui.grid.ColumnModel
 */
Zarafa.plugins.files.settings.ui.AccountGridColumnModel = Ext.extend(Zarafa.common.ui.grid.ColumnModel, {

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			columns : this.createDefaultColumns(),
			defaults: {
				sortable: true
			}
		});

		Zarafa.plugins.files.settings.ui.AccountGridColumnModel.superclass.constructor.call(this, config);
	},

	/**
	 * Create an array of {@link Ext.grid.Column columns} which must be visible within
	 * the default view of this {@link Ext.grid.ColumnModel ColumnModel}.
	 *
	 * @return {Ext.grid.Column|Array} The array of columns
	 * @private
	 */
	createDefaultColumns: function () {
		return [
			new Ext.grid.RowNumberer(),
			{
				header   : dgettext('plugin_files', 'ID'),
				dataIndex: 'id',
				width    : 50,
				hidden   : true,
				sortable : false,
				tooltip  : dgettext('plugin_files', 'Sort by: ID')
			}, {
				header   : dgettext('plugin_files', 'Status'),
				dataIndex: 'status',
				width    : 40,
				sortable : false,
				renderer : Zarafa.plugins.files.settings.data.AccountRenderUtil.statusRenderer,
				tooltip  : dgettext('plugin_files', 'Sort by: Status')
			}, {
				header   : dgettext('plugin_files', 'Name'),
				dataIndex: 'name',
				flex     : 1,
				sortable : false,
				tooltip  : dgettext('plugin_files', 'Sort by: Name')
			}, {
				header   : dgettext('plugin_files', 'Backend'),
				dataIndex: 'backend',
				width    : 40,
				sortable : false,
				renderer : Zarafa.plugins.files.settings.data.AccountRenderUtil.backendRenderer,
				tooltip  : dgettext('plugin_files', 'Sort by: Backend')
			}, {
				xtype    : 'actioncolumn',
				header   : dgettext('plugin_files', 'Features'),
				dataIndex: 'backend_features',
				width    : 40,
				sortable : false,
				tooltip  : dgettext('plugin_files', 'Shows all available features of the backend.'),
				items    : [{
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.QUOTA], true),
					icon    : 'plugins/files/resources/icons/features/quota.png',
					tooltip : dgettext('plugin_files', 'Show quota information'),
					handler : this.showDialog.createDelegate(this, ['filesplugin.featurequotainfo'], 2)
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.VERSION_INFO], true),
					icon    : 'plugins/files/resources/icons/features/info.png',
					tooltip : dgettext('plugin_files', 'Show version information'),
					handler : this.showDialog.createDelegate(this, ['filesplugin.featureversioninfo'], 2)
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.SHARING], true),
					icon    : 'plugins/files/resources/icons/features/sharing.png',
					tooltip : dgettext('plugin_files', 'Share files')
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.STREAMING], true),
					icon    : 'plugins/files/resources/icons/features/streaming.png',
					tooltip : dgettext('plugin_files', 'Fast Down/Upload')
				}]
			}];
	},

	/**
	 * This method gets called if the user clicks on the quota icon.
	 * It will then display the {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel quota panel}.
	 *
	 * @param {Object} grid the grid
	 * @param {Number} rowIndex used to retrieve the selected record
	 * @param {String} componentType the component to show
	 */
	showDialog: function(grid, rowIndex, componentType) {
		var record = grid.getStore().getAt(rowIndex);
		if (record.get('status') !== 'ok') {
			return;
		}

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType[componentType], undefined, {
			store  : grid.getStore(),
			item   : record,
			manager: Ext.WindowMgr
		});
	}
});
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountPanel
 * @extends Ext.grid.GridPanel
 * @xtype filesplugin.accountpanel
 * The main gridpanel for our data
 */
Zarafa.plugins.files.settings.ui.AccountPanel = Ext.extend(Ext.Panel, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			border: false,
			layout: 'fit',
			items : this.createPanelItems(config)
		});

		Zarafa.plugins.files.settings.ui.AccountPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.AccountPanel AccountPanel}.
	 *
	 * @param {Array} config config passed to the constructor
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function (config) {
		return [{
			xtype : 'container',
			layout: {
				type : 'vbox',
				align: 'stretch',
				pack : 'start'
			},
			items : [{
				xtype: "filesplugin.accountgrid",
				flex : 1
			}]
		}];
	}
});
Ext.reg('filesplugin.accountpanel', Zarafa.plugins.files.settings.ui.AccountPanel);Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.featurequotainfocontentpanel
 */
Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param {Object} config configuration object with the account.
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.featurequotainfocontentpanel',
			title     : dgettext('plugin_files', 'Quota Information'),
			statefull : false,
			width     : 200,
			autoHeight: true,
			items     : [{
				xtype: 'filesplugin.featurequotainfopanel',
				item : config.item
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.featurequotainfocontentpanel', Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel
 * @extends Ext.Panel
 * @xtype filesplugin.featurequotainfopanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel FeatureQuotaInfoContentPanel}.
 */
Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Object} The current loaded account record.
	 */
	account: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.account = config.item;
		}

		Ext.applyIf(config, {
			xtype      : 'filesplugin.featurequotainfopanel',
			items      : this.createPanelItems(),
			buttons    : [{
				text   : dgettext('plugin_files', 'Reload'),
				handler: this.doReload.createDelegate(this),
				scope  : this
			}, {
				text   : dgettext('plugin_files', 'Close'),
				handler: this.doClose,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel.superclass.constructor.call(this, config);

		this.doReload();
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * Reload the quota store.
	 */
	doReload: function () {
		var responseHandler = new Zarafa.plugins.files.data.ResponseHandler({
			successCallback: this.gotQuotaValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getquota',
			{
				accountId: this.account.get("id"),
				folder   : "/"
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It sets the quota information in the form panel.
	 *
	 * @param {Object} response object from the server
	 */
	gotQuotaValues: function (response) {
		if (!this.formPanel) {
			return;
		}

		var used = parseInt(response["quota"][0].amount);
		var available = parseInt(response["quota"][1].amount);
		// Backend sometimes returns a negative value for available data, set it to zero.
		if (used < 0 ) {
			used = 0;
		}

		if (available < 0) {
			available = 0;
		}

		this.formPanel.getForm().setValues([
			{ id: 'usedField', value: Ext.util.Format.fileSize(used) },
			{ id: 'availableField', value: Ext.util.Format.fileSize(available) },
			{ id: 'totalField', value: Ext.util.Format.fileSize(available + used) }
		]);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel FeatureQuotaInfoPanel}.
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function () {
		return [{
			xtype: 'form',
			border: false,
			ref: 'formPanel',
			labelAlign: 'left',
			items: [{
				xtype     : 'displayfield',
				name      : 'usedField',
				fieldLabel: dgettext('plugin_files', 'Used'),
				value     : dgettext('plugin_files', 'Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				name      : 'availableField',
				fieldLabel: dgettext('plugin_files', 'Free'),
				value     : dgettext('plugin_files', 'Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				name      : 'totalField',
				fieldLabel: dgettext('plugin_files', 'Total'),
				value     : dgettext('plugin_files', 'Loading') + '&hellip;'
			}]
		}];
	}
});

Ext.reg('filesplugin.featurequotainfopanel', Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.featureversioninfocontentpanel
 */
Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.featureversioninfocontentpanel',

			layout    : 'fit',
			model     : true,
			autoSave  : false,
			width     : 400,
			autoHeight: true,
			title     : dgettext('plugin_files', 'Version Information'),
			items     : [{
				xtype: 'filesplugin.featureversioninfopanel',
				item : config.item
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.featureversioninfocontentpanel', Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel);Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel
 * @extends Ext.Panel
 * @xtype filesplugin.featureversioninfopanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel FeatureVersionInfoContentPanel}.
 */
Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Object} The current loaded account record.
	 */
	account: undefined,

	/**
	 * @constructor
	 * @param config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.account = config.item;
		}

		Ext.applyIf(config, {

			xtype      : 'filesplugin.featureversioninfopanel',
			autoHeight : true,
			layout     : 'fit',
			defaultType: 'textfield',
			items      : this.createPanelItems(config),
			buttons    : [{
				text   : dgettext('plugin_files', 'Close'),
				handler: this.doClose,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel.superclass.constructor.call(this, config);

		this.doReload();
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * Reload the version store.
	 */
	doReload: function () {
		var responseHandler = new Zarafa.plugins.files.data.ResponseHandler({
			successCallback: this.gotVersionValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getversion',
			{
				accountId: this.account.get("id")
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It will update the textfield values.
	 *
	 * @param response
	 */
	gotVersionValues: function (response) {

		this.backendVersionField.setValue(response.version.backend);
		this.serverVersionField.setValue(response.version.server);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel FeatureVersionInfoPanel}.
	 *
	 * @param {Object} config
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function (config) {
		return [{
			xtype     : 'form',
			autoHeight: true,
			labelWidth: 200,
			title     : dgettext('plugin_files', 'This account uses the following Backend:'),
			items     : [{
				xtype     : 'displayfield',
				ref       : '../backendVersionField',
				fieldLabel: dgettext('plugin_files', 'Backend version'),
				value     : dgettext('plugin_files', 'Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				ref       : '../serverVersionField',
				fieldLabel: dgettext('plugin_files', 'Storage server version'),
				value     : dgettext('plugin_files', 'Loading') + '&hellip;'
			}]
		}];
	}
});

Ext.reg('filesplugin.featureversioninfopanel', Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel);Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesContextNavigatorBuilder
 * @singleton
 *
 * This class provides static helper functions for the hierarchy tree panels.
 */
Zarafa.plugins.files.ui.FilesContextNavigatorBuilder =  {
	/**
	 * Returns the main navigation panel for the files context.
	 *
	 * @param context
	 * @returns {Object}
	 */
	getNavigatorTreePanelContainer: function(context) {
		var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		// load is called when the store was initialized
		accStore.on('load', this.addInitialPanels, this, {single: true});

		return {
			xtype  : 'zarafa.contextnavigation',
			context: context,
			bodyCssClass : "files_navbar_panel",
			items  : [],
			listeners: {
				'afterrender': function() {
					Zarafa.plugins.files.data.singleton.AccountStore.getStore().load();
				}
			}
		};
	},

	/**
	 * This function is called after the account store has loaded its data.
	 * It will add a new hierarchy tree panel for each active account.
	 *
	 * @param store
	 * @param records
	 * @param options
	 * @private
	 */
	addInitialPanels: function(store, records, options) {
		var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorPanel().centerPanel;
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();

		var filesNavPanel = navPanel.find('plugin', context);
		if(filesNavPanel[0].rendered) {
			store.each(function (account, index) {
				if (account.get('status') === Zarafa.plugins.files.data.AccountRecordStatus.OK) {
					filesNavPanel[0].add({
						xtype : 'panel',
						autoHeight : true,
						cls : 'zarafa-files-hierarchypanel-subpanel',
						iconCls: 'icon_16_logo_' + account.get('backend'),
						title  : dgettext('plugin_files', account.get('backend')),
						accId  : account.get('id'),
						items  : [{
							xtype        : 'filesplugin.navigatortreepanel',
							accountFilter: account.get('id'),
							ref          : '../../../filesNavigatorTreePanel_' + account.get('id')
						}]
					});
				}
			});

			filesNavPanel[0].doLayout();
		} else {
			// wait for the navpanel to render
			filesNavPanel[0].on('afterrender', this.addInitialPanels.createDelegate(this,[store, records, options]), null, {single: true})
		}
	},

	/**
	 * Removes the selection from all visible panels.
	 */
	unselectAllNavPanels: function() {
		var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		accStore.each(function(account,index){
			if(account.get('status') === Zarafa.plugins.files.data.AccountRecordStatus.OK) {
				var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(account.get('id'));
				if(navPanel) {
					navPanel.getSelectionModel().clearSelections();
				}
			}
		});
	},

	/**
	 * This function must be called! It sets up the eventlisteners for the account store to
	 * process update and remove events.
	 */
	setUpListeners: function() {
		var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		// we need to listen to the add event to create a new panel
		accStore.on('update', this.addNavigatorPanel);

		// remove deleted accounts from the panel
		accStore.on('remove', this.removeNavigatorPanel);

		// reorder account panels
		accStore.on('reorder', this.swapPanels, this);
	},

	/**
	 * Swap two account panels.
	 *
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 * TODO: maybe improve this - do not rebuild the whole navigation tree.
	 */
	swapPanels: function(a, b) {
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorPanel().centerPanel;
		var mainContainer = navPanel.find('plugin', context);

		if(mainContainer[0].rendered) {
			mainContainer[0].removeAll(); // remove all, they will be readded afterwards
			this.addInitialPanels(Zarafa.plugins.files.data.singleton.AccountStore.getStore());
		}
	},

	/**
	 * Called if the account store triggers the 'update' event.
	 * A new panel will be added if it does not exist already.
	 * It also checks the panel status to remove invalid account panels.
	 *
	 * @param store
	 * @param record
	 * @param index
	 * @returns {boolean}
	 * @private
	 */
	addNavigatorPanel: function(store, record, index) {
		// first check if this panel does already exist
		// this event will also be called on account updates
		var existingAccount = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(record.get('id'));
		if(Ext.isDefined(existingAccount)) {
			// check if the account has errors - if so remove it from the panel!
			if(record.get('status') === Zarafa.plugins.files.data.AccountRecordStatus.ERROR) {
				existingAccount.ownerCt.destroy();
			} else {
				// update navtree UI

				// update account root node:
				var accRoot = existingAccount.getRootNode().item(0);
				if(Ext.isDefined(accRoot)) {
					accRoot.setText(record.get('name'));
					accRoot.setIconCls("icon_16_" + record.get('backend'));
				}
				// update the surrounding container
				existingAccount.ownerCt.setTitle(record.get('backend'));
				existingAccount.ownerCt.setIconClass("icon_16_logo_" + record.get('backend'));
			}
			return true;
		}

		if(record.get('status') === Zarafa.plugins.files.data.AccountRecordStatus.OK) {
			var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorPanel().centerPanel;
			var context = Zarafa.plugins.files.data.ComponentBox.getContext();

			var filesNavPanel = navPanel.find('plugin', context);

			if(filesNavPanel[0].rendered) {
				filesNavPanel[0].add({
					xtype  : 'panel',
					cls    : 'zarafa-context-navigation-block',
					iconCls: 'icon_16_logo_' + record.get('backend'),
					title  : dgettext('plugin_files', record.get('backend')),
					items  : [{
						xtype        : 'filesplugin.navigatortreepanel',
						accountFilter: record.get('id'),
						cls : 'files-context-navigation-node',
						ref          : '../../../filesNavigatorTreePanel_' + record.get('id')
					}]
				});
			}
		}
	},

	/**
	 * Called if the account store triggers the 'remove' event.
	 * The account tree panel will be removed from the main navigator panel.
	 *
	 * @param store
	 * @param record
	 * @param index
	 * @private
	 */
	removeNavigatorPanel: function(store, record, index) {
		var navPanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(record.get('id'));
		if(navPanel) {
			navPanel.ownerCt.destroy();
		}
	}
};
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesListToolbar
 * @extends Ext.Toolbar
 * @xtype filesplugin.fileslisttoolbar
 *
 * The top toolbar for the files explorer.
 */
Zarafa.plugins.files.ui.FilesListToolbar = Ext.extend(Ext.Toolbar, {
	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.core.ContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.mail.MailContextModel
	 */
	model: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			enableOverflow: true,
			items         : [{
				xtype       : 'zarafa.toolbarbutton',
				cls         : 'files_icon_actionbutton',
				text        : dgettext('plugin_files', 'Upload'),
				ref         : 'uploadButton',
				tooltip     : {
					title: dgettext('plugin_files', 'Upload file'),
					text : dgettext('plugin_files', 'Upload one or more files')
				},
				overflowText: dgettext('plugin_files', 'Upload files'),
				iconCls     : 'files_icon_action files_icon_action_upload',
				handler     : this.onFileUpload.createDelegate(this),
				model       : config.model
			}, {
				xtype       : 'zarafa.toolbarbutton',
				cls         : 'files_icon_actionbutton',
				text        : dgettext('plugin_files', 'Create folder'),
				ref         : 'createFolderButton',
				tooltip     : {
					title: dgettext('plugin_files', 'Create folder'),
					text : dgettext('plugin_files', 'Create a new folder')
				},
				overflowText: dgettext('plugin_files', 'Create new folder'),
				iconCls     : 'files_icon_action files_icon_action_new_folder',
				handler     : this.onCreateFolder,
				model       : config.model
			}, {
				xtype                  : 'zarafa.toolbarbutton',
				cls                    : 'files_icon_actionbutton',
				text                   : dgettext('plugin_files', 'Download'),
				onRecordSelectionChange: function (model, records) {
					this.setDisabled(!Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, false, true, true));
				},
				tooltip                : {
					title: dgettext('plugin_files', 'Download files'),
					text : dgettext('plugin_files', 'Download the selected files')
				},
				overflowText           : dgettext('plugin_files', 'Download files'),
				iconCls                : 'files_icon_action files_icon_action_download',
				handler                : this.onFileDownload,
				model                  : config.model
			}, {
				xtype                  : 'zarafa.toolbarbutton',
				cls                    : 'files_icon_actionbutton',
				text                   : dgettext('plugin_files', 'Share'),
				onRecordSelectionChange: function (model, records) {
					this.setDisabled(!Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, true, false, true));

					var visible = false;
					if (Ext.isDefined(records) && records.length > 0) {
						var account = records[0].getAccount();
						visible = account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.SHARING);
					}

					this.setVisible(visible);
				},
				tooltip                : {
					title: dgettext('plugin_files', 'Share files'),
					text : dgettext('plugin_files', 'Share the selected files')
				},
				overflowText           : dgettext('plugin_files', 'Share files'),
				iconCls                : 'files_icon_action files_icon_action_share',
				handler                : this.onFileShare,
				model                  : config.model
			}, {
				xtype                  : 'zarafa.toolbarbutton',
				cls                    : 'files_icon_actionbutton',
				text                   : dgettext('plugin_files', 'Attach to mail'),
				onRecordSelectionChange: function (model, records) {
					this.setDisabled(!Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, false, true, true));
				},
				tooltip                : {
					title: dgettext('plugin_files', 'Attach to mail'),
					text : dgettext('plugin_files', 'Attach the selected files to mail')
				},
				overflowText           : dgettext('plugin_files', 'Attach to mail'),
				iconCls                : 'files_icon_action files_icon_action_attach_to_mail',
				handler                : this.onFileAddToMail.createDelegate(this),
				model                  : config.model
			}, {
				xtype: 'tbfill'
			}, {
				xtype                  : 'zarafa.toolbarbutton',
				tooltip                : dgettext('plugin_files', 'Rename'),
				overflowText           : dgettext('plugin_files', 'Rename'),
				iconCls                : 'files_icon_action files_icon_action_edit',
				onRecordSelectionChange: function (model, records) {
					this.setDisabled(!Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, true, false, true));
				},
				nonEmptySelectOnly     : true,
				handler                : this.onRename,
				model                  : config.model
			}, {
				xtype             : 'zarafa.toolbarbutton',
				tooltip           : dgettext('plugin_files', 'Delete'),
				overflowText      : dgettext('plugin_files', 'Delete'),
				iconCls           : 'files_icon_action files_icon_action_delete',
				nonEmptySelectOnly: true,
				handler           : this.onDelete,
				model             : config.model
			}]
		});
		Zarafa.plugins.files.ui.FilesListToolbar.superclass.constructor.call(this, config);
	},

	/**
	 * Event handler for opening the "create new folder" dialog.
	 *
	 * @param button
	 * @param event
	 */
	onCreateFolder: function (button, event) {
		Zarafa.plugins.files.data.Actions.createFolder(this.model);
	},

	/**
	 * Event handler for opening the Browser's file selection dialog.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onFileUpload: function (button, event) {
		var uploadComponent = new Zarafa.plugins.files.ui.UploadComponent({
			callback: this.uploadCallback,
			multiple: true,
			scope   : this
		});

		uploadComponent.openUploadDialog();
	},

	/**
	 * Event handler for downloading the selected files.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onFileDownload: function (button, event) {
		var records = this.model.getSelectedRecords();
		Zarafa.plugins.files.data.Actions.openFilesContent(records);
	},

	/**
	 * Event handler for sharing the selected files.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onFileShare: function (button, event) {
		var records = this.model.getSelectedRecords();
		Zarafa.plugins.files.data.Actions.createShareDialog(records);
	},

	/**
	 * Event handler for attaching the selected files to a new mail record.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onFileAddToMail: function (button, event) {
		var records = button.model.getSelectedRecords();

		var emailRecord = container.getContextByName("mail").getModel().createRecord();
		var idsList = [];
		var attachmentStore = emailRecord.getAttachmentStore();

		Ext.each(records, function (record) {
			idsList.push(record.get('id'));
		}, this);

		container.getNotifier().notify('info.files', dgettext('plugin_files', 'Attaching'), dgettext('plugin_files', 'Creating email... Please wait!'));

		try {
			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'downloadtotmp',
				{
					ids               : idsList,
					maxAttachmentSize : container.getServerConfig().getMaxAttachmentSize(),
					dialog_attachments: attachmentStore.getId()
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.attachToMail.createDelegate(this, [emailRecord], true)
				})
			);
		} catch (e) {
			Zarafa.plugins.files.data.Actions.msgWarning(e.message);
		}
	},

	/**
	 * The callback function of {@link Zarafa.plugins.files.ui.UploadComponent}
	 * which used to upload the attachment file on server.
	 *
	 * @param {Object/Array} files The files contains file information.
	 * @param {Object} form the form is contains {@link Ext.form.BasicForm bacisform} info.
	 */
	uploadCallback: function (files, form) {
		Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, Zarafa.plugins.files.data.ComponentBox.getStore());
	},

	/**
	 * This method will add the downloaded files to a new mail record.
	 *
	 * @param responseItems
	 * @param response
	 * @param emailRecord
	 */
	attachToMail: function (responseItems, response, emailRecord) {
		Zarafa.plugins.files.data.Actions.openCreateMailContent(emailRecord, responseItems);
	},

	/**
	 * Event handler for renaming a selected file.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onRename: function (button, event) {
		var records = this.model.getSelectedRecords();

		Zarafa.plugins.files.data.Actions.openRenameDialog(this.model, records[0]);
	},

	/**
	 * Event handler for deleting files and folders.
	 *
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @param {Ext.Button} button the button on which click event is performed.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onDelete: function (button, event) {
		var records = this.model.getSelectedRecords();

		Zarafa.plugins.files.data.Actions.deleteRecords(records);
	},

	/**
	 * Function which is use to enable toolbar buttons
	 * It will enable upload and create folder button.
	 */
	enableButtons : function ()
	{
		this.createFolderButton.setDisabled(false);
		this.uploadButton.setDisabled(false);
	}
});

Ext.reg('filesplugin.fileslisttoolbar', Zarafa.plugins.files.ui.FilesListToolbar);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesMainContextMenu = Ext.extend(Zarafa.core.ui.menu.ConditionalMenu, {

	context: undefined,

	model: undefined,

	grid: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			items: [
				this.createContextActionItems(),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.contextmenu.actions', this),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.contextmenu.options', this)
			]
		});

		Zarafa.plugins.files.ui.FilesMainContextMenu.superclass.constructor.call(this, config);
	},

	createContextActionItems: function () {
		return [{
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Download'),
			iconCls   : 'files_icon_action files_icon_action_download',
			handler   : this.onContextItemOpen,
			beforeShow: function (item, records) {
				var visible = Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, false, true, false);

				item.setVisible(visible);
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Share'),
			iconCls   : 'files_icon_action files_icon_action_share',
			handler   : this.onContextItemShare,
			beforeShow: function (item, records) {
				var visible = false;
				var isShared = false;
				if (records.length > 0) {
					var account = records[0].getAccount();
					isShared = records[0].get("isshared");
					visible = account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.SHARING);
				}

				visible = visible && Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, true, false, true);

				item.setVisible(visible);

				if (isShared == true) {
					item.setText(dgettext('plugin_files', 'Edit share'));
				}
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Attach to mail'),
			iconCls   : 'files_icon_action files_icon_action_attach_to_mail',
			handler   : this.onContextItemAttach,
			beforeShow: function (item, records) {
				var visible = Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, false, true, true);
				var server = container.getServerConfig();

				var max_attachment_size = server.getMaxAttachmentSize();

				var max_largefile_size = max_attachment_size;

				var large_files_enabled = false;

				if (typeof server.isLargeFilesEnabled === 'function') {
					max_largefile_size = server.getMaxLargefileSize();
					large_files_enabled = server.isLargeFilesEnabled();
				}

				for (var i = 0; i < records.length; i++) {
					var record = records[i];
					if (!large_files_enabled && record.get('message_size') > max_attachment_size) {
						visible = false;
						break;
					} else if (large_files_enabled && record.get('message_size') > max_largefile_size) {
						visible = false;
						break;
					}
				}

				item.setVisible(visible);
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Rename'),
			iconCls   : 'files_icon_action files_icon_action_edit',
			handler   : this.onContextItemRename,
			beforeShow: function (item, records) {
				item.setVisible(Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, true, false, true));
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Delete'),
			iconCls   : 'files_icon_action files_icon_action_delete',
			handler   : this.onContextItemDelete,
			beforeShow: function (item, records) {
				item.setVisible(Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, false, false, true));
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Info'),
			iconCls   : 'icon_info',
			disabled  : Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentViewMode() != Zarafa.plugins.files.data.ViewModes.NO_PREVIEW,
			handler   : this.onContextItemInfo,
			beforeShow: function (item, records) {
				item.setVisible(Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(records, true, false, true));
			},
			scope     : this
		}];
	},

	onContextItemOpen: function () {
		Zarafa.plugins.files.data.Actions.openFilesContent(this.records);
	},

	onContextItemDelete: function () {
		Zarafa.plugins.files.data.Actions.deleteRecords(this.records);
	},

	onContextItemShare: function () {
		Zarafa.plugins.files.data.Actions.createShareDialog(this.records);
	},

	onContextItemInfo: function () {
		var count = this.records.length;
		var record = undefined;

		if (count == 1) {
			record = this.records[0];
		}

		var config = Ext.applyIf({}, {
			modal : true,
			record: record
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.fileinfopanel'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	onContextItemRename: function () {
		Zarafa.plugins.files.data.Actions.openRenameDialog(this.model, this.records[0]);
	},

	onContextItemAttach: function () {
		var emailRecord = container.getContextByName("mail").getModel().createRecord();
		var idsList = [];
		var attachmentStore = emailRecord.getAttachmentStore();

		Ext.each(this.records, function (record) {
			idsList.push(record.get('id'));
		}, this);

		container.getNotifier().notify('info.files', dgettext('plugin_files', 'Attaching'), dgettext('plugin_files', 'Creating email... Please wait!'));

		try {
			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'downloadtotmp',
				{
					ids               : idsList,
					maxAttachmentSize : container.getServerConfig().getMaxAttachmentSize(),
					dialog_attachments: attachmentStore.getId()
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.attachToMail.createDelegate(this, [emailRecord], true)
				})
			);
		} catch (e) {
			Zarafa.plugins.files.data.Actions.msgWarning(e.message);
		}
	},

	attachToMail: function (responseItems, response, emailRecord) {
		Zarafa.plugins.files.data.Actions.openCreateMailContent(emailRecord, responseItems);
	}
});

Ext.reg('filesplugin.filesmaincontextmenu', Zarafa.plugins.files.ui.FilesMainContextMenu);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesMainPanel = Ext.extend(Zarafa.common.ui.ContextMainPanel, {

	viewPanel: undefined,

	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.filesmainpanel',
			layout: 'zarafa.switchborder',

			items: [
				this.initMainItems(config),
				this.initPreviewPanel(config.context)
			],
			tbar       : {
				xtype: 'filesplugin.filestoptoolbar',
				height      : 28,
				context     : config.context
			}
		});

		Zarafa.plugins.files.ui.FilesMainPanel.superclass.constructor.call(this, config);
	},

	initMainItems: function (config) {
		return {
			xtype      : 'panel',
			ref        : 'filesViewPanel',
			layout     : 'zarafa.collapsible',
			cls        : 'zarafa-context-mainpanel',
			minWidth   : 200,
			minHeight  : 200,
			region     : 'center',
			collapsible: false,
			split      : true,
			items      : [{
				xtype    : 'zarafa.switchviewcontentcontainer',
				ref      : '../viewPanel',
				layout   : 'card',
				lazyItems: this.initViews(config.context)
			}],
			tbar       : {
				xtype       : 'filesplugin.fileslisttoolbar',
				defaultTitle: dgettext('plugin_files', 'Files'),
				height      : 33,
				context     : config.context
			}
		};
	},

	initViews: function (context) {

		var allViews = [{
			xtype  : 'filesplugin.filesrecordaccountview',
			flex   : 1,
			id     : 'files-accountview',
			anchor : '100%',
			context: context
		}, {
			xtype  : 'filesplugin.filesrecordgridview',
			flex   : 1,
			id     : 'files-gridview',
			anchor : '100%',
			context: context
		}, {
			xtype  : 'filesplugin.filesrecordiconview',
			flex   : 1,
			id     : 'files-iconview',
			anchor : '100%',
			context: context
		}];

		var additionalViewItems = container.populateInsertionPoint('plugin.files.views', this, context);
		allViews = allViews.concat(additionalViewItems);

		return allViews;
	},

	initPreviewPanel: function (context) {
		return {
			xtype  : 'filesplugin.filespreviewpanel',
			ref    : 'filesPreview',
			region : 'south',
			split  : true,
			context: context
		};
	},

	/**
	 * Called during rendering of the panel, this will initialize all events.
	 * @private
	 */
	initEvents: function () {
		if (Ext.isDefined(this.context)) {
			this.mon(this.context, 'viewchange', this.onViewChange, this);
			this.mon(this.context, 'viewmodechange', this.onViewModeChange, this);

			this.onViewChange(this.context, this.context.getCurrentView());
			this.onViewModeChange(this.context, this.context.getCurrentViewMode());
		}

		Zarafa.plugins.files.ui.FilesMainPanel.superclass.initEvents.apply(this, arguments);
	},

	onViewChange: function (context, newView, oldView) {
		switch (newView) {
			case Zarafa.plugins.files.data.Views.LIST:
				this.viewPanel.switchView('files-gridview');
				break;
			case Zarafa.plugins.files.data.Views.ICON:
				this.viewPanel.switchView('files-iconview');
				break;
		}
	},

	onViewModeChange: function (context, newViewMode, oldViewMode) {
		var orientation;

		switch (newViewMode) {
			case Zarafa.plugins.files.data.ViewModes.NO_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.OFF;
				break;
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.HORIZONTAL;
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.VERTICAL;
				break;
			case Zarafa.plugins.files.data.ViewModes.SEARCH:
				return;
		}

		var layout = this.getLayout();
		if (!Ext.isFunction(layout.setOrientation)) {
			if (Ext.isString(layout)) {
				this.layoutConfig = Ext.apply(this.layoutConfig || {}, {orientation: orientation});
			} else {
				this.layout.orientation = orientation;
			}
		} else {
			layout.setOrientation(orientation);
		}
	}
});

Ext.reg('filesplugin.filesmainpanel', Zarafa.plugins.files.ui.FilesMainPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesPreviewPanel
 * @extends Ext.Panel
 * @xtype filesplugin.filespreviewpanel
 *
 * The preview panel container for the files preview.
 */
Zarafa.plugins.files.ui.FilesPreviewPanel = Ext.extend(Ext.Panel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		var toolbar = Ext.applyIf(config.tbar || {}, {
			cls   : 'zarafa-previewpanel-toolbar',
			xtype : 'zarafa.toolbar',
			height: 33,
			hidden: false,
			items : []
		});

		Ext.applyIf(config, {
			xtype   : 'filesplugin.filespreviewpanel',
			layout  : 'fit',
			stateful: true,
			cls     : 'zarafa-previewpanel zarafa-context-mainpanel',
			width   : 300,
			height  : 300,
			tbar    : toolbar
		});

		Zarafa.plugins.files.ui.FilesPreviewPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filespreviewpanel', Zarafa.plugins.files.ui.FilesPreviewPanel);

Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordAccountView = Ext.extend(Zarafa.common.ui.DraggableDataView, {

	context: undefined,

	model: undefined,

	keyMap: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}
		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		config.store = Ext.StoreMgr.lookup(config.store);

		config.plugins = Ext.value(config.plugins, []);
		config.plugins.push('zarafa.icondragselectorplugin');

		Ext.applyIf(config, {
			xtype: 'filesplugin.filesrecordaccountview',

			cls           : 'zarafa-files-accountview',
			loadingText   : dgettext('plugin_files', 'Loading accounts') + '...',
			deferEmptyText: false,
			autoScroll    : true,
			emptyText     : '<div class="emptytext">' + dgettext('plugin_files', 'There are no accounts added. Go to settings and add an account!') + '</div>',
			overClass     : 'zarafa-files-accountview-over',
			tpl           : this.initTemplate(),
			multiSelect   : true,
			selectedClass : 'zarafa-files-accountview-selected',
			itemSelector  : 'div.zarafa-files-accountview-container'
		});

		Zarafa.plugins.files.ui.FilesRecordAccountView.superclass.constructor.call(this, config);

		this.initEvents();
	},

	initTemplate: function () {
		// Load the account store


		return new Ext.XTemplate(
			'<div style="height: 100%; width: 100%; overflow: auto;">',
			'<tpl for=".">',
			'<div class="zarafa-files-accountview-container">',
			'<div class="zarafa-files-account-background {.:this.getAccountType}"> </div>',
			'<div class="zarafa-files-account-info">',
			'<span class="zarafa-files-accountview-subject">{filename:htmlEncode}</span>',
			'<span class="zarafa-files-accountview-account">{.:this.getAccountIdentifier}</span>',
			'</div>',
			'</div>',
			'</tpl>',
			'</div>',
			{
				getAccountType: function (record) {
					// get an instance of the account store.
					var store = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

					// get the account id from the path string
					var accId = Zarafa.plugins.files.data.Utils.File.getAccountId(record.id);

					// look up the account
					var account = store.getById(accId);

					var backend = "Webdav"; // Default is webdav...
					if (Ext.isDefined(account)) {
						backend = account.get("backend");
					}

					return "icon_256_" + backend;
				},

				getAccountIdentifier: function (record) {
					// get an instance of the account store.
					var store = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

					// get the account id from the path string
					var accId = Zarafa.plugins.files.data.Utils.File.getAccountId(record.id);

					// look up the account
					var account = store.getById(accId);

					var identifier = ""; // Default is empty...
					// TODO: this is not dynamic because the backend_config variable names might change in other backends
					if (Ext.isDefined(account)) {
						var bconfig = account.get("backend_config");
						if(bconfig && Ext.isDefined(bconfig.user) && Ext.isDefined(bconfig.server_address)) {
							identifier = bconfig.user + "@" + bconfig.server_address;
						}
					}

					return Zarafa.plugins.files.data.Utils.Format.truncate(identifier, 27); // 27 = length of the account field
				}
			}
		);
	},

	getMainPanel: function () {
		return this.ownerCt;
	},

	initEvents: function () {
		this.on({
			'dblclick'       : this.onIconDblClick,
			'selectionchange': this.onSelectionChange,
			'afterrender'    : this.onAfterRender,
			scope            : this
		});
	},

	onAfterRender: function () {
		this.keyMap = new Ext.KeyMap(this.getEl(), {
			key: Ext.EventObject.DELETE,
			fn : this.onKeyDelete.createDelegate(this)
		});
	},

	onKeyDelete: function (key, event) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : dgettext('plugin_files', 'Error'),
			msg    : dgettext('plugin_files', 'To delete an account you have to go to settings.'),
			icon   : Zarafa.common.dialogs.MessageBox.ERROR,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});
	},

	onIconDblClick: function (dataview, index, node, event) {
		var record = this.getStore().getAt(index);
		Zarafa.plugins.files.data.Actions.openFilesContent([record]);
	},

	onSelectionChange: function (dataView, selections) {
		if (this.context.getCurrentViewMode() != Zarafa.plugins.files.data.ViewModes.NO_PREVIEW) {
			this.model.setPreviewRecord(undefined);
		}
	}
});

Ext.reg('filesplugin.filesrecordaccountview', Zarafa.plugins.files.ui.FilesRecordAccountView);

Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordDetailsPanel = Ext.extend(Ext.form.FormPanel, {

	defaultPreviewImage: 'plugins/files/resources/icons/no-preview.jpg',

	record: undefined,

	constructor: function (config) {
		config = config || {};
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();

		var layout = {
			type : 'vbox',
			align: 'stretch',
			pack : 'start'
		};
		switch (viewMode) {
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				layout = {
					type : 'hbox',
					align: 'stretch',
					pack : 'start'
				};
				break;
			default:
				break;
		}

		config = Ext.applyIf(config, {
			xtype      : 'filesplugin.filesrecorddetailspanel',
			ref        : '../fileinfo',
			autoDestroy: true,
			layout     : layout,
			border     : false,
			items      : [
				this.fieldSetFileInfo(),
				this.fieldSetFilePreview()
			]
		});

		if (Ext.isDefined(config.record)) {
			this.record = config.record;
			config = Ext.applyIf(config, {
				listeners: {
					afterlayout: function (cmp) {
						this.update(this.record);
					}
				}
			});
		}

		Zarafa.plugins.files.ui.FilesRecordDetailsPanel.superclass.constructor.call(this, config);
	},

	refresh: function () {
		this.removeAll();
		this.add(this.fieldSetFileInfo());
		this.add(this.fieldSetFilePreview());
	},

	fieldSetFileInfo: function () {
		return {
			xtype   : 'fieldset',
			title   : dgettext('plugin_files', 'File information'),
			height  : 150,
			width   : 300,
			defaults: {
				anchor: '-3'
			},
			items   : [{
				xtype     : 'textfield',
				fieldLabel: dgettext('plugin_files', 'Filename'),
				ref       : '../filename',
				value     : "unknown",
				readOnly  : true
			},
				{
					xtype     : 'textfield',
					fieldLabel: dgettext('plugin_files', 'Filesize'),
					ref       : '../filesize',
					value     : "unknown",
					readOnly  : true
				},
				{
					xtype     : 'textfield',
					fieldLabel: dgettext('plugin_files', 'Last modified'),
					ref       : '../lastmodified',
					value     : "unknown",
					readOnly  : true
				},
				{
					xtype     : 'textfield',
					fieldLabel: dgettext('plugin_files', 'Type'),
					ref       : '../type',
					value     : "unknown",
					readOnly  : true
				},
				{
					xtype     : 'textfield',
					fieldLabel: dgettext('plugin_files', 'Is shared'),
					ref       : '../shared',
					hidden    : true,
					value     : "unknown",
					readOnly  : true
				}]
		};
	},

	fieldSetFilePreview: function () {
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();

		var css = "width: 100%;";
		switch (viewMode) {
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				css = "width: 100%;";
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				css = "height: 100%;";
				break;
			default:
				break;
		}

		return {
			xtype: 'fieldset',
			title: dgettext('plugin_files', 'File preview'),
			ref  : 'filepreview',
			flex : 1,
			autoScroll: true,

			defaultType: 'textfield',
			items      : [{
				xtype : 'component',
				id    : 'previewimage',
				autoEl: {tag: 'img', src: this.defaultPreviewImage, style: css}
			}]
		};
	},

	setPreviewPanel: function (record, extension) {
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();
		var fileviewerEnabled = Ext.isDefined(container.getPluginByName('filepreviewer')) ? true: false;
		var pdfEnabled = Ext.isDefined(container.getPluginByName('pdfbox')) ? true: false;
		var odfEnabled = Ext.isDefined(container.getPluginByName('webodf')) ? true: false;

		var css = "width: 100%;";
		switch (viewMode) {
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				css = "width: 100%;";
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				css = "height: 100%;";
				break;
			default:
				break;
		}

		var component = {};

		if (!fileviewerEnabled && !Ext.isEmpty(extension) && (/\.(gif|jpg|jpeg|tiff|png|bmp)$/i).test(extension)) {
			component = {
				xtype : 'component',
				autoEl: {tag: 'img', src: Zarafa.plugins.files.data.Actions.getDownloadLink(record), style: css}
			}
		} else if (fileviewerEnabled && !Ext.isEmpty(extension) && (new RegExp(container.getSettingsModel().get("zarafa/v1/plugins/filepreviewer/supported_filetypes"), "i")).test(extension)) {
			component = {
				xtype   : 'filepreviewer.viewerpanel',
				record: record,
				defaultScale: 1,
				autoResize: this.filepreview, // autoresize on this element
				height: this.filepreview.getInnerHeight()
			}
		} else if (!fileviewerEnabled && pdfEnabled && !Ext.isEmpty(extension) && (/\.(pdf)$/i).test(extension)) {
			component = {
				xtype   : 'filesplugin.pdfjspanel',
				src     : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
				title: record.get('filename')
			}
		} else if (!fileviewerEnabled && !pdfEnabled && !Ext.isEmpty(extension) && (/\.(pdf)$/i).test(extension)) { // if the pdfjs plugin is not available
			// show the pdf file in an iframe
			component = {
				xtype  : 'component',
				autoEl : {
					tag: 'iframe',
					width: '98%',
					height: '98%',
					border: 'none',
					seamless: '',
					src: Zarafa.plugins.files.data.Actions.getDownloadLink(record)
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(txt|html|php|js|c|cpp|h|java|sh|bat|log|cfg|conf|tex|py|pl)$/i).test(extension)) {
			component = {
				xtype    : 'textarea',
				hideLabel: true,
				readOnly : true,
				anchor   : '0, 0',
				listeners: {
					'afterrender': function () {
						Ext.Ajax.request({
							method : 'GET',
							url    : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							success: function (result, request) {
								var responsetext = result.responseText;

								this.setRawValue(responsetext);
							},
							scope  : this
						});
					}
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(mp3|wav)$/i).test(extension)) {
			var audioType = '';
			switch(extension.toLowerCase()) {
				case '.wav':
					audioType = 'audio/wav';
					break;
				default:
					audioType = 'audio/mpeg';
			}

			component = {
				xtype : 'component',
				autoEl: {
					tag: 'audio',
					style: css,
					controls: 'controls',
					cn    : [
						{
							tag: 'source',
							src: Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							type: audioType
						},
						dgettext('plugin_files', 'Your browser does not support previewing of audio files!')
					]
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(mp4|ogg|webm)$/i).test(extension)) {
			var videoType = '';
			switch(extension.toLowerCase()) {
				case '.ogg':
					videoType = 'video/ogg';
					break;
				case '.webm':
					videoType = 'video/webm';
					break;
				default:
					videoType = 'audio/mp4';
			}

			component = {
				xtype : 'component',
				autoEl: {
					tag: 'video',
					style: css + 'height: auto;',
					poster: 'plugins/files/resources/images/preview/video_loader.gif',
					preload: 'metadata',
					controls: 'controls',
					cn    : [
						{
							tag: 'source',
							src: Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							type: videoType
						},
						dgettext('plugin_files', 'Your browser does not support previewing of video files!')
					]
				}
			}
		} else if (odfEnabled && !Ext.isEmpty(extension) && (/\.(odp|odt|ods)$/i).test(extension)) {
			component = {
				xtype : 'filesplugin.webodfpanel',
				src   : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
				title : record.get('filename')
			}
		} else {
			component = {
				xtype : 'component',
				autoEl: {tag: 'img', src: this.defaultPreviewImage, style: css}
			}
		}

		this.filepreview.removeAll(true);
		this.filepreview.add(component);
		this.filepreview.doLayout();
	},

	update: function (record) {

		var extension = this.getExtension(record.get('filename'));

		this.filename.setValue(record.get('filename'));
		if (record.get('type') == Zarafa.plugins.files.data.FileTypes.FILE) {
			this.filesize.show();
			this.filesize.setValue(Zarafa.plugins.files.data.Utils.Format.fileSize(record.get('message_size')));
		} else {
			this.filesize.hide();
		}
		this.lastmodified.setValue(Ext.util.Format.date(new Date(record.get('lastmodified')), dgettext('plugin_files', 'd.m.Y G:i')));
		this.type.setValue(record.get('type') == Zarafa.plugins.files.data.FileTypes.FILE ? String.format(dgettext('plugin_files', 'File ({0})'), extension) : dgettext('plugin_files', 'Folder'));

		if (record.getAccount().supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.SHARING)) {
			this.shared.show();
			this.shared.setValue(record.get("isshared") ? dgettext('plugin_files', 'Yes') : dgettext('plugin_files', 'No'));
		} else {
			this.shared.hide();
		}
		this.setPreviewPanel(record, extension);
	},

	onRender: function (ct, position) {
		Zarafa.plugins.files.ui.FilesRecordDetailsPanel.superclass.onRender.call(this, ct, position);
		this.wrap = this.el.wrap({cls: 'preview-body'});
		this.resizeEl = this.positionEl = this.wrap;
	},

	getExtension: function (filename) {
		var i = filename.lastIndexOf('.');
		return (i < 0) ? '' : filename.substr(i);
	}
});

Ext.reg('filesplugin.filesrecorddetailspanel', Zarafa.plugins.files.ui.FilesRecordDetailsPanel);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordGridColumnModel = Ext.extend(Zarafa.common.ui.grid.ColumnModel, {

	useCompactView: false,

	constructor: function (config) {
		config = config || {};

		this.defaultColumns = this.createDefaultColumns();
		this.compactColumns = this.createCompactColumns();

		Ext.applyIf(config, {
			columns : this.defaultColumns,
			defaults: {
				sortable: true
			}
		});

		if (config.useCompactView === true) {
			config.columns = this.compactColumns;
		}

		Ext.apply(this, config);

		Zarafa.plugins.files.ui.FilesRecordGridColumnModel.superclass.constructor.call(this, config);
	},

	createDefaultColumns: function () {
		return [{
			id       : 'type',
			dataIndex: 'type',
			header   : '<p class="icon_index">&nbsp;</p>',
			headerCls: 'zarafa-icon-column icon',
			renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
			width    : 24,
			fixed    : true,
			tooltip  : dgettext('plugin_files', 'Sort by: Type')
		},
			{
				header   : 'ID',
				dataIndex: 'id',
				width    : 50,
				hidden   : true,
				tooltip  : dgettext('plugin_files', 'Sort by: ID')
			},
			{
				header   : 'Path',
				dataIndex: 'path',
				width    : 100,
				hidden   : true,
				tooltip  : dgettext('plugin_files', 'Sort by: Path')
			},
			{
				header   : dgettext('plugin_files', 'Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : dgettext('plugin_files', 'Sort by: Filename')
			},
			{
				header   : dgettext('plugin_files', 'Last modified'),
				dataIndex: 'lastmodified',
				width    : 160,
				renderer : Zarafa.plugins.files.data.Utils.Renderer.datetimeRenderer,
				tooltip  : dgettext('plugin_files', 'Sort by: Last modified')
			},
			{
				header   : dgettext('plugin_files', 'Size'),
				dataIndex: 'message_size',
				width    : 80,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : dgettext('plugin_files', 'Sort by: Size')
			},
			{
				id       : 'isshared',
				dataIndex: 'isshared',
				header   : '<p class="files_icon_12_share">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.sharedRenderer,
				listeners: {
					click: this.doOnShareButtonClick
				},
				width    : 32,
				fixed    : true,
				tooltip  : dgettext('plugin_files', 'Sort by: Shared')
			}
		];
	},

	createCompactColumns: function () {
		return [{
			id       : 'column_type',
			dataIndex: 'type',
			header   : '<p class="icon_index">&nbsp;</p>',
			headerCls: 'zarafa-icon-column icon',
			renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
			width    : 24,
			fixed    : true,
			tooltip  : dgettext('plugin_files', 'Sort by: Type')
		},
			{
				header   : dgettext('plugin_files', 'Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : dgettext('plugin_files', 'Sort by: Filename')
			},
			{
				header   : dgettext('plugin_files', 'Last modified'),
				dataIndex: 'lastmodified',
				width    : 100,
				renderer : Zarafa.plugins.files.data.Utils.Renderer.datetimeRenderer,
				tooltip  : dgettext('plugin_files', 'Sort by: Last modified')
			},
			{
				header   : dgettext('plugin_files', 'Size'),
				dataIndex: 'message_size',
				width    : 80,
				hidden   : true,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : dgettext('plugin_files', 'Sort by: Size')
			},
			{
				id       : 'isshared',
				dataIndex: 'isshared',
				header   : '<p class="files_icon_12_share">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.sharedRenderer,
				listeners: {
					click: this.doOnShareButtonClick
				},
				width    : 32,
				fixed    : true,
				tooltip  : dgettext('plugin_files', 'Sort by: Shared')
			}
		];
	},

	setCompactView: function (compact) {
		if (this.useCompactView !== compact) {
			this.useCompactView = compact;

			if (compact) {
				this.name = 'compact';

				this.defaultColumns = this.config;
				this.columns = this.compactColumns;
			} else {
				this.name = 'default';
				this.compactColumns = this.config;
				this.columns = this.defaultColumns;
			}

			this.setConfig(this.columns, false);
		}
	},

	doOnShareButtonClick: function (col, grid, row, event) {
		var record = grid.store.getAt(row);

		if (record.get("isshared") === true) {
			Zarafa.plugins.files.data.Actions.createShareDialog([record]);
		}
	}
});
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordGridView = Ext.extend(Zarafa.common.ui.grid.GridPanel, {

	context: undefined,

	model: undefined,

	dropTarget: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		config.store = Ext.StoreMgr.lookup(config.store);

		Ext.applyIf(config, {
			xtype                     : 'filesplugin.filesrecordgridview',
			ddGroup                   : 'dd.filesrecord',
			id                        : 'files-gridview',
			enableDragDrop            : true,
			border                    : false,
			stateful                  : true,
			statefulRelativeDimensions: false,
			loadMask                  : this.initLoadMask(),
			viewConfig                : this.initViewConfig(),
			sm                        : this.initSelectionModel(),
			cm                        : this.initColumnModel(),
			keys                      : {
				key    : Ext.EventObject.DELETE,
				handler: this.onKeyDelete,
				scope  : this
			}
		});
		Zarafa.plugins.files.ui.FilesRecordGridView.superclass.constructor.call(this, config);

	},

	initEvents: function () {
		Zarafa.plugins.files.ui.FilesRecordGridView.superclass.initEvents.call(this);

		this.mon(this, 'cellcontextmenu', this.onCellContextMenu, this);
		this.mon(this, 'rowbodycontextmenu', this.onRowBodyContextMenu, this);
		this.mon(this, 'rowdblclick', this.onRowDblClick, this);
		this.mon(this, 'afterrender', this.initDropTarget, this);

		this.mon(this.getSelectionModel(), 'beforerowselect', this.onBeforeRowSelect, this, {buffer: 1});
		this.mon(this.getSelectionModel(), 'rowselect', this.onRowSelect, this, {buffer: 1});
		this.mon(this.getSelectionModel(), 'selectionchange', this.onSelectionChange, this, {buffer: 1});

		this.mon(this.context, 'viewmodechange', this.onContextViewModeChange, this);
		this.onContextViewModeChange(this.context, this.context.getCurrentViewMode());
	},

	initLoadMask: function () {
		return {
			msg: dgettext('plugin_files', 'Loading files') + '...'
		};
	},

	initViewConfig: function () {
		return {

			enableRowBody: false,

			rowSelectorDepth: 15
		};
	},

	initSelectionModel: function () {
		return new Ext.grid.RowSelectionModel();
	},

	initColumnModel: function () {
		return new Zarafa.plugins.files.ui.FilesRecordGridColumnModel();
	},

	initDropTarget: function () {
		var gridDropTargetEl = this.getView().el.dom.childNodes[0].childNodes[1];

		gridDropTargetEl.addEventListener("dragstart", function (e) {
			e.dataTransfer.effectAllowed = "copy";
			e.preventDefault();
			e.stopPropagation();
		}, false);

		gridDropTargetEl.addEventListener("dragenter", function (e) {
			e.preventDefault();
			e.stopPropagation();
		}, false);

		gridDropTargetEl.addEventListener("dragover", function (e) {
			e.dataTransfer.dropEffect = "copy";
			e.preventDefault();
			e.stopPropagation();
		}, false);

		gridDropTargetEl.addEventListener("dragleave", function (e) {
			e.preventDefault();
			e.stopPropagation();
		}, false);

		gridDropTargetEl.addEventListener("drop", function (e) {
			e.preventDefault();
			e.stopPropagation();

			var dt = e.dataTransfer;
			var files = dt.files;

			Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, Zarafa.plugins.files.data.ComponentBox.getStore());

			return false;
		}, false);

		this.dropTarget = new Ext.dd.DropTarget(gridDropTargetEl, {
			ddGroup   : 'dd.filesrecord',
			copy      : false,
			gridStore : this.getStore(),
			gridSM    : this.getSelectionModel(),
			notifyDrop: function (ddSource, e, data) {

				if (this.notifyOver(ddSource, e, data) !== this.dropAllowed) {
					return false;
				}

				var cellindex = ddSource.getDragData(e).rowIndex;
				var dropTarget = this.gridStore.getAt(cellindex);
				if (Ext.isDefined(cellindex) && dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {

					Ext.each(data.selections, function (record) {
						record.setDisabled(true);
					});

					return Zarafa.plugins.files.data.Actions.moveRecords(data.selections, dropTarget);
				} else {
					return false;
				}
			},
			notifyOver: function (ddSource, e, data) {
				var cellindex = ddSource.getDragData(e).rowIndex;
				var ret = this.dropNotAllowed;

				if (Ext.isDefined(cellindex)) {
					var dropTarget = this.gridStore.getAt(cellindex);

					if (dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
						ret = this.dropAllowed;
					}

					Ext.each(data.selections, function (record) {
						var srcId = record.get("id");
						var trgId = dropTarget.get("id");
						if (srcId === trgId || record.get("filename") === ".." || trgId.slice(0, srcId.length) === srcId) {
							ret = this.dropNotAllowed;
							return false;
						}
					}, this);
				}

				return ret;
			},

			notifyEnter: function (ddSource, e, data) {
				return this.notifyOver(ddSource, e, data);
			}
		});

		this.getView().dragZone.onBeforeDrag = function (data, e) {
			var ret = true;
			var selectedRowInSelection = false;
			var selectedItem = data.grid.getStore().getAt(data.rowIndex);

			Ext.each(data.selections, function (record) {
				if (selectedItem.get("id") === record.get("id")) {
					selectedRowInSelection = true;
				}
				if (record.get("filename") === ".." || record.getDisabled()) {
					ret = false;
					return false;
				}
			});

			if (selectedRowInSelection) {
				return ret;
			} else {

				if (selectedItem.get("filename") === ".." || selectedItem.getDisabled()) {
					return false;
				} else {
					return true;
				}
			}
		}
	},

	onContextViewModeChange: function (context, newViewMode, oldViewMode) {
		var compact = newViewMode === Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW;

		this.getColumnModel().setCompactView(compact);
	},

	onCellContextMenu: function (grid, rowIndex, cellIndex, event) {
		var sm = this.getSelectionModel();
		var cm = this.getColumnModel();

		if (sm.hasSelection()) {

			if (!sm.isSelected(rowIndex)) {

				sm.clearSelections();
				sm.selectRow(rowIndex);
			}
		} else {

			sm.selectRow(rowIndex);
		}

		var column = {};

		if (cellIndex >= 0) {
			column = cm.getColumnById(cm.getColumnId(cellIndex));
		}

		var records = sm.getSelections();

		var show = true;
		Ext.each(records, function (record) {
			if (record.getDisabled() === true) {
				show = false;
				return;
			}
		});

		if (show) {
			Zarafa.core.data.UIFactory.openDefaultContextMenu(records, {
				position: event.getXY(),
				context : this.context,
				grid    : this
			});
		}
	},

	onRowBodyContextMenu: function (grid, rowIndex, event) {
		this.onCellContextMenu(grid, rowIndex, -1, event);
	},

	onRowDblClick: function (grid, rowIndex, e) {
		var record = grid.store.getAt(rowIndex);
		Zarafa.plugins.files.data.Actions.openFilesContent([record]);
	},

	onKeyDelete: function (key, event) {
		var selections = this.getSelectionModel().getSelections();

		Zarafa.plugins.files.data.Actions.deleteRecords(selections);
	},

	onBeforeRowSelect: function (sm, rowIndex, keepExisting, record) {
		return !record.getDisabled();
	},

	onRowSelect: function (selectionModel, rowNumber, record) {
		var viewMode = this.context.getCurrentViewMode();

		var count = selectionModel.getCount();

		if (viewMode != Zarafa.plugins.files.data.ViewModes.NO_PREVIEW) {
			if (count == 0) {
				this.model.setPreviewRecord(undefined);
			} else if (count == 1 && selectionModel.getSelected() === record) {
				if (record.get('id') !== (container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/") && record.get('filename') !== "..") {
					this.model.setPreviewRecord(record);
				} else {
					this.model.setPreviewRecord(undefined);
				}
			}
		}
	},

	onSelectionChange: function (selectionModel) {
		var selections = selectionModel.getSelections();
		var viewMode = this.context.getCurrentViewMode();

		this.model.setSelectedRecords(selections);
		if (viewMode !== Zarafa.plugins.files.data.ViewModes.NO_PREVIEW) {
			if (Ext.isEmpty(selections)) {
				this.model.setPreviewRecord(undefined);
			}
		}
	}
});

Ext.reg('filesplugin.filesrecordgridview', Zarafa.plugins.files.ui.FilesRecordGridView);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordIconView = Ext.extend(Zarafa.common.ui.DraggableDataView, {

	context: undefined,

	model: undefined,

	dropTarget: undefined,

	keyMap: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}
		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		config.store = Ext.StoreMgr.lookup(config.store);

		config.plugins = Ext.value(config.plugins, []);
		config.plugins.push('zarafa.icondragselectorplugin');

		Ext.applyIf(config, {
			xtype: 'filesplugin.filesrecordiconview',

			cls           : 'zarafa-files-iconview',
			loadingText   : dgettext('plugin_files', 'Loading files') + '...',
			deferEmptyText: false,
			autoScroll    : true,
			emptyText     : '<div class="emptytext">' + dgettext('plugin_files', 'There are no items to show in this view') + '</div>',
			overClass     : 'zarafa-files-iconview-over',
			tpl           : this.initTemplate(),
			multiSelect   : true,
			selectedClass : 'zarafa-files-iconview-selected',
			itemSelector  : 'div.zarafa-files-iconview-thumb',
			enableDragDrop: true,
			ddGroup       : 'dd.filesrecord'
		});

		Zarafa.plugins.files.ui.FilesRecordIconView.superclass.constructor.call(this, config);

		this.initEvents();
	},

	initTemplate: function () {
		return new Ext.XTemplate(
			'<div style="height: 100%; width: 100%; overflow: auto;">',
			'<tpl for=".">',
			'<div class="zarafa-files-iconview-container {.:this.getHidden}">',
			'<div class="zarafa-files-iconview-thumb {.:this.getTheme} {.:this.getHidden}">',
			'</div>',
			'<span class="zarafa-files-iconview-subject">{filename:htmlEncode}</span>',
			'</div>',
			'</tpl>',
			'</div>',
			{

				getHidden: function (record) {
					if (record.filename === "..") {
						return "files_type_hidden";
					}

					return "";
				},

				getTheme: function (record) {

					switch (record.type) {
						case Zarafa.plugins.files.data.FileTypes.FOLDER:
							return Zarafa.plugins.files.data.Utils.File.getIconClass("folder");
							break;
						case Zarafa.plugins.files.data.FileTypes.FILE:
							return Zarafa.plugins.files.data.Utils.File.getIconClass(record.filename);
							break;
						default:
							return 'files48icon_blank';
							break;
					}
				}
			}
		);
	},

	getMainPanel: function () {
		return this.ownerCt;
	},

	initEvents: function () {
		this.on({
			'contextmenu'    : this.onFilesIconContextMenu,
			'dblclick'       : this.onIconDblClick,
			'selectionchange': this.onSelectionChange,
			'afterrender'    : this.onAfterRender,
			scope            : this
		});
	},

	onAfterRender: function ()
	{
		// Add key maps only while keyboard shortcut is enable
		if (Zarafa.core.KeyMapMgr.isGloballyEnabled()) {
			this.keyMap = new Ext.KeyMap(this.getEl(), {
				key: Ext.EventObject.DELETE,
				fn: this.onKeyDelete.createDelegate(this)
			});

			// Disable all other key maps for this element
			Zarafa.core.KeyMapMgr.disableAllKeymaps(this.getEl());
		}

		this.initDropTarget();
	},

	onKeyDelete: function (key, event) {
		var selections = this.getSelectedRecords();
		Zarafa.plugins.files.data.Actions.deleteRecords(selections);
	},

	initDropTarget: function () {
		var iconViewDropTargetEl = this.getEl();

		iconViewDropTargetEl.dom.addEventListener("dragstart", function (e) {
			e.dataTransfer.effectAllowed = "copy";
			e.preventDefault();
			e.stopPropagation();
		}, false);

		iconViewDropTargetEl.dom.addEventListener("dragenter", function (e) {
			e.preventDefault();
			e.stopPropagation();
		}, false);

		iconViewDropTargetEl.dom.addEventListener("dragover", function (e) {
			e.dataTransfer.dropEffect = "copy";
			e.preventDefault();
			e.stopPropagation();
		}, false);

		iconViewDropTargetEl.dom.addEventListener("dragleave", function (e) {
			e.preventDefault();
			e.stopPropagation();
		}, false);

		iconViewDropTargetEl.dom.addEventListener("drop", function (e) {
			e.preventDefault();
			e.stopPropagation();

			var dt = e.dataTransfer;
			var files = dt.files;

			Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, Zarafa.plugins.files.data.ComponentBox.getStore());

			return false;
		}, false);

		this.dropTarget = new Ext.dd.DropTarget(iconViewDropTargetEl, {
			ddGroup    : 'dd.filesrecord',
			copy       : false,
			fileStore  : this.getStore(),
			notifyDrop : function (ddSource, e, data) {

				if (this.notifyOver(ddSource, e, data) !== this.dropAllowed) {
					return false;
				}

				var dragData = ddSource.getDragData(e);

				if (Ext.isDefined(dragData)) {
					var cellindex = dragData.index;
					var dropTarget = this.fileStore.getAt(cellindex);
					if (Ext.isDefined(cellindex) && dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {

						Ext.each(data.selections, function (record) {
							record.setDisabled(true);
						});

						return Zarafa.plugins.files.data.Actions.moveRecords(data.selections, dropTarget);
					}
				}

				return false;
			},
			notifyOver : function (ddSource, e, data) {
				var dragData = ddSource.getDragData(e);
				var ret = this.dropNotAllowed;

				if (Ext.isDefined(dragData)) {
					var cellindex = dragData.index;

					if (Ext.isDefined(cellindex)) {
						var dropTarget = this.fileStore.getAt(cellindex);

						if (dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
							ret = this.dropAllowed;
						}

						Ext.each(data.selections, function (record) {
							var srcId = record.get("id");
							var trgId = dropTarget.get("id");
							if (srcId === trgId || record.get("filename") === ".." || trgId.slice(0, srcId.length) === srcId) {
								ret = this.dropNotAllowed;
								return false;
							}
						}, this);
					}
				}
				return ret;
			},
			notifyEnter: function (ddSource, e, data) {
				return this.notifyOver(ddSource, e, data);
			}
		});

		this.dragZone.onBeforeDrag = function (data, e) {
			var ret = true;
			var selectedRowInSelection = false;
			var selectedItem = this.view.getStore().getAt(data.index);

			Ext.each(data.selections, function (record) {
				if (selectedItem.get("id") === record.get("id")) {
					selectedRowInSelection = true;
				}
				if (record.getDisabled()) {
					ret = false;
					return false;
				}
			});

			if (selectedRowInSelection) {
				return ret;
			} else {

				if (selectedItem.getDisabled()) {
					return false;
				} else {
					return true;
				}
			}
		}
	},

	onFilesIconContextMenu: function (dataview, index, node, eventObj) {

		if (!dataview.isSelected(node)) {
			dataview.select(node);
		}

		var records = dataview.getSelectedRecords();

		var show = true;
		Ext.each(records, function (record) {
			if (record.getDisabled() === true) {
				show = false;
				return;
			}
		});

		if (show) {
			Zarafa.core.data.UIFactory.openDefaultContextMenu(records, {
				position: eventObj.getXY(),
				context : this.context
			});
		}
	},

	onIconDblClick: function (dataview, index, node, event) {
		var record = this.getStore().getAt(index);
		Zarafa.plugins.files.data.Actions.openFilesContent([record]);
	},

	onSelectionChange: function (dataView, selections) {
		this.model.setSelectedRecords(dataView.getSelectedRecords());

		var viewMode = this.context.getCurrentViewMode();

		var records = dataView.getSelectedRecords();
		var count = records.length;

		if (viewMode != Zarafa.plugins.files.data.ViewModes.NO_PREVIEW) {
			if (count != 1) {
				this.model.setPreviewRecord(undefined);
			} else if (count == 1) {
				if (records[0].get('id') !== (container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/") && records[0].get('filename') !== "..") {
					this.model.setPreviewRecord(records[0]);
				} else {
					this.model.setPreviewRecord(undefined);
				}
			}
		}
	}
});

Ext.reg('filesplugin.filesrecordiconview', Zarafa.plugins.files.ui.FilesRecordIconView);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordViewPanel = Ext.extend(Ext.Panel, {

	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.filesrecordviewpanel',
			border: false,
			cls   : 'zarafa-filesviewpanel',
			layout: 'zarafa.collapsible',
			items : [{
				xtype: 'filesplugin.filesrecorddetailspanel'
			}]
		});

		Zarafa.plugins.files.ui.FilesRecordViewPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filesrecordviewpanel', Zarafa.plugins.files.ui.FilesRecordViewPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesTopToolbar
 * @extends Ext.Toolbar
 * @xtype filesplugin.filestoptoolbar
 *
 * The top toolbar for the files explorer.
 */
Zarafa.plugins.files.ui.FilesTopToolbar = Ext.extend(Ext.Toolbar, {
	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.core.ContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.mail.MailContextModel
	 */
	model: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			cls: 'files_top_toolbar',
			items: [{
				xtype: 'filesplugin.navigationbar'
			}, {
				xtype: 'tbfill'
			}, {
				xtype: 'filesplugin.quotabar'
			}]
		});
		Zarafa.plugins.files.ui.FilesTopToolbar.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filestoptoolbar', Zarafa.plugins.files.ui.FilesTopToolbar);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesTreeContextMenu = Ext.extend(Zarafa.core.ui.menu.ConditionalMenu, {

	context: undefined,

	model: undefined,

	records: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		if (Ext.isDefined(config.records)) {
			this.records = config.records;
		}

		Ext.applyIf(config, {
			items: [
				this.createContextActionItems(),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.treecontextmenu.actions', this),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.treecontextmenu.options', this)
			]
		});

		Zarafa.plugins.files.ui.FilesTreeContextMenu.superclass.constructor.call(this, config);
	},

	createContextActionItems: function () {
		return [{
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Rename'),
			iconCls   : 'files_icon_action files_icon_action_edit',
			handler   : this.onContextItemRename,
			beforeShow: function (item, records) {
				var rec = records[0];
				var path = Zarafa.plugins.files.data.Utils.File.stripAccountId(rec.get('id'));
				item.setVisible(path != "/");
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Delete'),
			iconCls   : 'files_icon_action files_icon_action_delete',
			handler   : this.onContextItemDelete,
			beforeShow: function (item, records) {
				var rec = records[0];
				var path = Zarafa.plugins.files.data.Utils.File.stripAccountId(rec.get('id'));
				item.setVisible(path != "/");
			},
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : dgettext('plugin_files', 'Create folder'),
			iconCls   : 'files_icon_action files_icon_action_new_folder',
			handler   : this.onContextItemNewFolder,
			scope     : this
		}];
	},

	onContextItemDelete: function (menuitem, event) {
		Zarafa.plugins.files.data.Actions.deleteRecords(this.records);
	},

	onContextItemNewFolder: function (menuitem, event) {
		var clickedRecord = this.records[0];

		Zarafa.plugins.files.data.Actions.createFolder(this.model, null, clickedRecord.get('id'));
	},

	onContextItemRename: function (menuitem, event) {
		Zarafa.plugins.files.data.Actions.openRenameDialog(this.model, this.records[0]);
	}
});

Ext.reg('filesplugin.filestreecontextmenu', Zarafa.plugins.files.ui.FilesTreeContextMenu);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.MultipleFileUploadField
 * @extends Ext.ux.form.FileUploadField
 * @xtype filesplugin.multiplefileuploadfield
 *
 * Creates a file upload field.
 */
Zarafa.plugins.files.ui.MultipleFileUploadField = Ext.extend(Ext.ux.form.FileUploadField, {
	// override
	createFileInput: function () {
		var opt = {
			id  : this.getFileInputId(),
			name: this.name || this.getId(),
			cls : 'x-form-file',
			tag : 'input',
			type: 'file',
			size: 1
		};

		opt.multiple = 'multiple';
		this.fileInput = this.wrap.createChild(opt);
	},

	// override
	bindListeners  : function () {
		this.fileInput.on({
			scope     : this,
			mouseenter: function () {
				this.button.addClass(['x-btn-over', 'x-btn-focus'])
			},
			mouseleave: function () {
				this.button.removeClass(['x-btn-over', 'x-btn-focus', 'x-btn-click'])
			},
			mousedown : function () {
				this.button.addClass('x-btn-click')
			},
			mouseup   : function () {
				this.button.removeClass(['x-btn-over', 'x-btn-focus', 'x-btn-click'])
			},
			change    : function () {
				var v = this.fileInput.dom.value;
				if (this.fileInput.dom.files.length > 1) {
					v = this.fileInput.dom.files.length + ' ' + dgettext('plugin_files', 'files selected');
				}
				this.setValue(v);
				this.fireEvent('fileselected', this, v);
			}
		});
	}
});

Ext.reg('filesplugin.multiplefileuploadfield', Zarafa.plugins.files.ui.MultipleFileUploadField);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.Tree
 * @extends Ext.tree.TreePanel
 * @xtype filesplugin.tree
 *
 * The hierarchy tree panel implementation for files.
 */
Zarafa.plugins.files.ui.Tree = Ext.extend(Ext.tree.TreePanel, {

	/**
	 * @cfg {array|String} array of account ids or a single account id that should be loaded.
	 */
	accountFilter: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.accountFilter)) {
			this.accountFilter = config.accountFilter;
		}

		Ext.applyIf(config, {
			xtype : 'filesplugin.tree',
			enableDD : true,
			ddGroup : 'dd.filesrecord',
			ddAppendOnly : true,
			pathSeparator: '&',
			root : {
				nodeType: 'async',
				text : 'Files',
				id : '#R#',
				expanded: true,
				cc : false
			},
			rootVisible : false,
			autoScroll : true,
			maskDisabled : true,
			loader : new Zarafa.plugins.files.data.NavigatorTreeLoader({
				loadfiles : false,
				accountFilter: this.accountFilter
			})
		});
		Zarafa.plugins.files.ui.Tree.superclass.constructor.call(this, config);
	},

	/**
	 * Function which is create the {@link Zarafa.plugins.files.data.FilesRecord record} from
	 * give tree node.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node asynchronous tree node.
	 * @return {Zarafa.plugins.files.data.FilesRecord} returns folder record.
	 */
	convertNodeToRecord : function (node)
	{
		var fileRecord = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, {
			'id' : node.attributes.id,
			'filename' : node.attributes.filename,
			'path' : node.attributes.path,
			'type' : node.attributes.isFolder ? Zarafa.plugins.files.data.FileTypes.FOLDER : Zarafa.plugins.files.data.FileTypes.FILE,
			'entryid' : node.attributes.id,
			'message_class' : 'IPM.Files'
		},node.attributes.id);
		fileRecord.store = Zarafa.plugins.files.data.ComponentBox.getStore();
		return fileRecord;
	},

	/**
	 * Recursive function to select a node. The whole path to the node will be expanded.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node asynchronous tree node.
	 * @param {Ext.tree.AsyncTreeNode} childnode asynchronous tree child node.
	 */
	selectNode: function (node, childnode) {
		Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.unselectAllNavPanels();

		if (!Ext.isDefined(childnode)) {
			this.nodeToSelect = node.id;
		}

		if (Ext.isDefined(childnode) && childnode.rendered) {
			childnode.select();
			childnode.expand();
		} else if (Ext.isDefined(node) && node.rendered) {
			node.select();
			node.expand();
		} else {

			var parentNode = node.id.replace(/\\/g, '/').replace(/\/[^\/]*\/?$/, '') + "/";
			var nToSelectParent = this.getNodeById(parentNode);
			if (Ext.isDefined(nToSelectParent)) {
				nToSelectParent.on("expand", this.selectNode.createDelegate(this, [node], true), this, {single: true});
			}
		}
	}
});

Ext.reg('filesplugin.tree', Zarafa.plugins.files.ui.Tree);
Ext.namespace('Zarafa.plugins.files.ui');
/**
 * @class Zarafa.plugins.files.ui.UploadComponent
 * @extends Ext.Component
 * @xtype filesplugin.uploadcomponent
 */
Zarafa.plugins.files.ui.UploadComponent = Ext.extend(Ext.Component, {
	/**
	 * @cfg {Function} callback The callback function which must be called when the
	 * file has be selected from Browser's file selection dialog.
	 */
	callback: Ext.emptyFn,

	/**
	 * @cfg {Object} scope The scope for the {@link #callback} function
	 */
	scope: undefined,

	/**
	 * @cfg {Boolean} multiple The multiple true to allow upload multiple files
	 * else allow single file only. by default it is false.
	 */
	multiple: false,

	/**
	 * @cfg {String} accept the accept define which type of files allow to
	 * show in Browser's file selection dialog. i.e image/* to allow all type of images.
	 */
	accept: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype: 'filesplugin.uploadcomponent'
		});

		Zarafa.plugins.files.ui.UploadComponent.superclass.constructor.call(this, config);
	},

	/**
	 * Event handler for opening the Browser's file selection dialog.
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @private
	 */
	openUploadDialog: function () {
		var uploadEl = this.getUploadEl();

		// Register the change event handler
		// so we detect when the user selects a file.
		uploadEl.on('change', this.onFileInputChange, this);

		// Mimick clicking on the <input> field
		// to open the File Selection dialog.
		uploadEl.dom.click();
	},

	/**
	 * Obtain or instantiate the {@link Ext.Element attachment} &lt;input&gt; element used
	 * for opening the File selection dialog.
	 * @return {Ext.Element} The file input element
	 * @private
	 */
	getUploadEl: function () {
		var uploadEl = Ext.DomHelper.append(Ext.getBody(), {
			cls : 'x-hidden',
			tag : 'input',
			type: 'file'
		});

		if (Ext.isDefined(this.multiple) && this.multiple) {
			uploadEl.multiple = this.multiple;
		}

		if (Ext.isDefined(this.accept)) {
			uploadEl.accept = this.accept;
		}

		uploadEl = Ext.get(uploadEl);
		return uploadEl;
	},

	/**
	 * Event handler which is fired when the {@link #attachEl} has been changed.
	 * @param {Ext.EventObject} event The event
	 * @private
	 */
	onFileInputChange: function (event) {
		var browserEvent = event.browserEvent;
		var uploadEl = Ext.get(browserEvent.target);
		var transfer = browserEvent.dataTransfer;
		var transferFile = transfer ? transfer.files : undefined;
		var files = uploadEl.dom.files || transferFile;

		this.callback.call(this.scope, files);

		// remove attachment element.
		uploadEl.remove();
	}
});

Ext.reg('filesplugin.uploadcomponent', Zarafa.plugins.files.ui.UploadComponent);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.attachfromfilescontentpanel
 *
 * This content panel contains the download tree panel for attaching item to emails.
 */
Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @var object
	 */
	record: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout     : 'fit',
			title      : dgettext('plugin_files', 'Add attachment from Files'),
			closeOnSave: true,
			width      : 400,
			height     : 300,

			items: [{
				xtype      : 'filesplugin.attachfromfilestreepanel',
				emailrecord: config.emailrecord,
				ref        : 'treePanel'
			}]
		});

		Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.attachfromfilescontentpanel', Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel
 * @extends Ext.tree.TreePanel
 * @xtype filesplugin.attachfromfilestreepanel
 *
 * This dialog panel will provide the filechooser tree.
 */
Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel = Ext.extend(Ext.tree.TreePanel, {

	/**
	 * @var {Zarafa.core.data.IPMRecord} emailRecord
	 */
	emailRecord: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.emailrecord)) {
			this.emailRecord = config.emailrecord;
		}

		Ext.applyIf(config, {
			root        : {
				nodeType: 'async',
				text    : 'Files',
				id      : '#R#',
				expanded: true,
				cc      : false
			},
			rootVisible : false,
			autoScroll  : true,
			viewConfig  : {
				style: {overflow: 'auto', overflowX: 'hidden'}
			},
			listeners   : {
				expandnode: this.onExpandNode
			},
			maskDisabled: true,
			loader      : new Zarafa.plugins.files.data.NavigatorTreeLoader({loadfiles: true}),
			buttons     : [
				this.createActionButtons()
			]
		});
		Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel.superclass.constructor.call(this, config);
	},

	/**
	 * The {@link Ext.tree.TreePanel#expandnode} event handler. It will silently load the children of the node.
	 * This is used to check if a node can be expanded or not.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node
	 */
	onExpandNode: function (node) {
		node.attributes["cc"] = true;
		node.eachChild(function (child) {
			if (child.attributes["cc"] !== true) { // only check if it was not checked before
				child.attributes["cc"] = true;
				child.quietLoad();
			}
		});
	},

	/**
	 * Genereate the toolbar buttons.
	 *
	 * @returns {Object}
	 */
	createActionButtons: function () {
		return [{
			xtype  : 'button',
			text   : '&nbsp;&nbsp;&nbsp;&nbsp;' + dgettext('plugin_files', 'Add attachment'),
			tooltip: {
				title: dgettext('plugin_files', 'Add attachment'),
				text : dgettext('plugin_files', 'Add selected attachment from files to email attachment.')
			},
			iconCls: 'icon_files_category_white',
			handler: this.downloadSelectedFilesFromFilesToTmp,
			scope  : this
		}];
	},

	/**
	 * Start to download the files to a temporary folder on the backend.
	 */
	downloadSelectedFilesFromFilesToTmp: function () {
		var selectedNodes = this.getChecked();
		var idsList = [];
		var emailRecord = this.dialog.record;

		if (Ext.isDefined(this.emailRecord)) {
			emailRecord = this.emailRecord;
		}

		var attachmentStore = emailRecord.getAttachmentStore();
		var server = container.getServerConfig();
		var max_attachment_size = server.getMaxAttachmentSize();
		var max_largefile_size = max_attachment_size;
		var large_files_enabled = false;

		if (typeof server.isLargeFilesEnabled === 'function') {
			max_largefile_size = server.getMaxLargefileSize();
			large_files_enabled = server.isLargeFilesEnabled();
		}

		var size_exceeded = false;

		Ext.each(selectedNodes, function (node, index) {
			if (!large_files_enabled && node.attributes.filesize > max_attachment_size) {
				Zarafa.common.dialogs.MessageBox.show({
					title  : dgettext('plugin_files', 'Warning'),
					msg    : String.format(dgettext('plugin_files', 'The file {0} is too large!'), node.attributes.filename) + ' (' + dgettext('plugin_files', 'max') + ': ' + Ext.util.Format.fileSize(max_attachment_size) + ')',
					icon   : Zarafa.common.dialogs.MessageBox.WARNING,
					buttons: Zarafa.common.dialogs.MessageBox.OK
				});
				size_exceeded = true;
				return false;
			} else if (large_files_enabled && node.attributes.filesize > max_largefile_size) {
				Zarafa.common.dialogs.MessageBox.show({
					title  : dgettext('plugin_files', 'Warning'),
					msg    : String.format(dgettext('plugin_files', 'The file {0} is too large!'), node.attributes.filename) + ' (' + dgettext('plugin_files', 'max') + ': ' + Ext.util.Format.fileSize(max_largefile_size) + ')',
					icon   : Zarafa.common.dialogs.MessageBox.WARNING,
					buttons: Zarafa.common.dialogs.MessageBox.OK
				});
				size_exceeded = true;
				return false;
			}
			idsList.push(node.id);
		});

		if (!size_exceeded) {
			if (idsList.length < 1) {
				Ext.MessageBox.show({
					title  : dgettext('plugin_files', 'Warning'),
					msg    : dgettext('plugin_files', 'You have to choose at least one file!'),
					icon   : Zarafa.common.dialogs.MessageBox.WARNING,
					buttons: Zarafa.common.dialogs.MessageBox.OK
				});
			} else {
				try {
					this.disable();
					container.getRequest().singleRequest(
						'filesbrowsermodule',
						'downloadtotmp',
						{
							ids               : idsList,
							dialog_attachments: attachmentStore.getId()
						},
						new Zarafa.plugins.files.data.ResponseHandler({
							successCallback: this.addDownloadedFilesAsAttachmentToEmail.createDelegate(this)
						})
					);
				} catch (e) {
					Zarafa.common.dialogs.MessageBox.show({
						title  : dgettext('plugin_files', 'Warning'),
						msg    : e.getMessage(),
						icon   : Zarafa.common.dialogs.MessageBox.WARNING,
						buttons: Zarafa.common.dialogs.MessageBox.OK
					});
				}
			}
		}
	},

	/**
	 * Convert the serverresponse to {@link Ext.data.Record}.
	 *
	 * @param {Object} downloadedFileInfo
	 * @returns {Ext.data.Record}
	 */
	convertDownloadedFileInfoToAttachmentRecord: function (downloadedFileInfo) {
		var attachmentRecord = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.MAPI_ATTACH);

		attachmentRecord.set('tmpname', downloadedFileInfo.tmpname);
		attachmentRecord.set('name', downloadedFileInfo.name);
		attachmentRecord.set('size', downloadedFileInfo.size);
		return attachmentRecord;
	},

	/**
	 * Add the attachment records to the email.
	 *
	 * @param downloadedFilesInfoArray
	 */
	addDownloadedFilesAsAttachmentToEmail: function (downloadedFilesInfoArray) {
		var emailRecord = this.dialog.record;
		if (Ext.isDefined(this.emailRecord)) {
			emailRecord = this.emailRecord;
		}

		var attachmentStore = emailRecord.getAttachmentStore();
		var attachmentRecord = null;
		var isHtml = emailRecord.get("isHTML");

		var server = container.getServerConfig();

		var max_attachment_size = server.getMaxAttachmentSize();

		var large_files_enabled = (typeof server.isLargeFilesEnabled === 'function') ? server.isLargeFilesEnabled() : false;

		Ext.each(downloadedFilesInfoArray, function (downloadedFileInfo, index) {
			attachmentRecord = this.convertDownloadedFileInfoToAttachmentRecord(downloadedFileInfo);
			attachmentStore.add(attachmentRecord);
			if (large_files_enabled && attachmentRecord.get('size') > max_attachment_size) {

				var tabs = container.getTabPanel().items.items;
				Ext.each(tabs, function (tab, index) {

					if (tab instanceof Zarafa.mail.dialogs.MailCreateContentPanel) {
						if (tab.record === emailRecord) {

							var currentBody = tab.mainPanel.editorField.getRawValue();
							var lfLink = attachmentRecord.getLargeFileLink(isHtml);
							tab.mainPanel.editorField.setValue(lfLink + currentBody);
							return false;
						}
					}
				});
			}
		}, this);
		this.dialog.close();
	}
});

Ext.reg('filesplugin.attachfromfilestreepanel', Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.createfoldercontentpanel
 *
 * This content panel contains the files tree panel for create new folder in that.
 */
Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'zarafa.createfoldercontentpanel',
			layout: 'fit',
			title : dgettext('plugin_files', 'Create New Folder'),
			width: 300,
			height: 350,
			items: [{
				xtype: 'filesplugin.createfolderpanel',
				accountFilter : config.accountFilter,
				selectedFolderId : config.selectedFolderId
			}]
		});

		Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.createfoldercontentpanel', Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFolderPanel
 * @extends Ext.Panel
 * @xtype filesplugin.createfolderpanel
 *
 * Panel for users to create folder record in differnt supported backends.
 */
Zarafa.plugins.files.ui.dialogs.CreateFolderPanel = Ext.extend(Ext.Panel, {

	/**
	 * selectedFolderId was contains the id of selected folder in
	 * {@link Zarafa.plugins.files.ui.Tree FolderTree}
	 * @property
	 */
	selectedFolderId : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.createfolderpanel',
			layout: {
				type: 'fit',
				align: 'stretch'
			},
			border: false,
			header: false,
			items: this.createPanel(config.accountFilter),
			buttonAlign: 'right',
			buttons: [{
				text: dgettext('plugin_files', 'Ok'),
				disabled: true,
				ref: '../okButton',
				cls: 'zarafa-action',
				handler : this.onOk,
				scope: this
			},{
				text: dgettext('plugin_files', 'Cancel'),
				disabled: true,
				ref: '../cancelButton',
				handler : this.onCancel,
				scope: this
			}]
		});

		Zarafa.plugins.files.ui.dialogs.CreateFolderPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Creates body for {@link Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel CreateFolderContentPanel}
	 * @param {String} accountFilter which is used to load the selected account's {@link Zarafa.plugins.files.ui.Tree FolderTree}
	 * @return {Array} Array which contains configuration object for the tree panel.
	 * @private
	 */
	createPanel : function(accountFilter)
	{
		return [{
			xtype : 'panel',
			layout : 'form',
			border : false,
			defaults : {
				anchor :'100%'
			},
			labelAlign : 'top',
			items : [{
				xtype : 'textfield',
				fieldLabel : dgettext('plugin_files', 'Name'),
				cls: 'form-field-name',
				ref : '../newNameField'
			},{
				xtype : 'filesplugin.tree',
				bodyCssClass : 'files-create-folder-tree-panel',
				fieldLabel : dgettext('plugin_files', 'Select where to place the folder'),
				anchor : '100% 80%',
				forceLayout : true,
				ref : '../hierarchyTree',
				accountFilter : accountFilter
			}]
		}];
	},

	/**
	 * Function called by Extjs when the panel has been {@link #render rendered}.
	 * At this time all events can be registered.
	 * @private
	 */
	initEvents : function ()
	{
		Zarafa.plugins.files.ui.dialogs.CreateFolderPanel.superclass.initEvents.apply(this, arguments);
		this.mon(this.hierarchyTree, 'load', this.onTreeNodeLoad, this);
		this.mon(this.hierarchyTree.getSelectionModel(), 'selectionchange', this.onSelectionChange, this);
	},

	/**
	 * Event handler which is triggered when the user presses the cancel
	 * {@link Ext.Button button}. This will close this dialog.
	 * @private
	 */
	onCancel : function()
	{
		this.dialog.close();
	},

	/**
	 * Event handler which is triggered when the user presses the ok
	 * {@link Ext.Button button}. function is responsible to create folder
	 * under the respective folder as well as check for dublicate folder.
	 *
	 * @param {Ext.Button} button which triggeres this event.
	 * @param {Ext.EventObject} event The event object
	 */
	onOk : function (button, event)
	{
		this.okButton.disable();
		var folderName = this.newNameField.getValue();
		if (Ext.isEmpty(folderName.trim())) {
			Ext.MessageBox.show({
				title: dgettext('plugin_files', 'Kopano WebApp'),
				msg: dgettext('plugin_files', 'You must specify a name.'),
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.INFO,
				fn : function (button) {
					this.okButton.enable();
				},
				scope : this
			});
			return;
		}
		this.doCheckFolderDuplicate(folderName);
	},

	/**
	 * Function is responsible for checking that folders dublicated in folder tree.
	 *
	 * @param {String} folderName name of folder which user trying
	 * to create in folder tree.
 	 */
	doCheckFolderDuplicate : function(folderName)
	{
		//FIXME : 1. Why we send the seaprate request to check folder is already exist in backend.
		// rather to do so we can also check folder exist or not while creating folder on server side by this way
		// we can reduce one request to server.
		// 2. there are many unnecessary action types are used e.g checkifexists, createdir etc which we can remove by
		// effective use of request params.
		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'checkifexists',
			{
				records : [{
					id : this.selectedFolderId + folderName + '/',
					isFolder: true
				}],
				destination: this.selectedFolderId
			},
			new Zarafa.plugins.files.data.ResponseHandler({
				successCallback: this.checkForDuplicateDone.createDelegate(this, [folderName, this.selectedFolderId], true)
			})
		);
	},

	/**
	 * Callback function which show warning message if folder is already exists or
	 * folder name is incorrect else it will call {#doCreateFolder} function.
	 *
	 * @param {Object} response response object which received from server.
	 * @param {String} folderName Folder name which user trying
	 * to create in folder tree.
	 * @param {String} parentFolderId parent folder id under which user trying to create folder.
	 */
	checkForDuplicateDone : function(response, folderName, parentFolderId)
	{
		if (response.duplicate === true) {
			this.okButton.enable();
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'Folder already exists'));
		} else if (!Zarafa.plugins.files.data.Utils.File.isValidFilename(folderName)) {
			this.okButton.enable();
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'Incorrect foldername'));
		} else {
			this.doCreateFolder(folderName, parentFolderId);
		}
	},

	/**
	 * Function which responsible to create folder in selected folder tree.
	 *
	 * @param {String} folderName Folder name which user trying
	 * to create in folder tree.
	 * @param {String} parentFolderId parent folder id under
	 * which user trying to create folder.
	 */
	doCreateFolder : function(folderName, parentFolderId)
	{
		var d = new Date();
		var data = {
			"filename"    : folderName,
			"path"        : Zarafa.plugins.files.data.Utils.File.stripAccountId(parentFolderId).replace(/\/+$/, ''),
			"id"          : parentFolderId + folderName + "/",
			"message_size": -1,
			"lastmodified": d.toUTC().getTime(),
			"type"        : Zarafa.plugins.files.data.FileTypes.FOLDER
		};

		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'createdir',
			{
				props: data
			},
			new Zarafa.plugins.files.data.ResponseHandler({
				successCallback:this.createFolderDone.createDelegate(this, [folderName, parentFolderId], true)
			})
		);
	},

	/**
	 * Callback function which called after the creating folder in respective folder tree.
	 * It will append the node in hierarchy tree and update the grid with newly added folder record.
	 *
	 * @param {Object} response response object which received from server.
	 * @param {String} folderName Folder name which user trying
	 * to create in folder tree.
	 * @param {String} parentFolderId parent folder id under which user trying to create folder.
	 */
	createFolderDone: function (response, folderName, parentFolderId)
	{
		if (!Ext.isDefined(response.item[0])) {
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'Folder could not be created!'));
			return false;
		}

		var props = response.item[0].props;

		var newFolder = {
			id          : props.id,
			text        : props.filename,
			filename    : props.filename,
			has_children: false,
			expanded    : true,
			iconCls     : 'icon_folder_note',
			loaded      : true,
			isFolder    : true
		};

		var d = new Date();
		var data = {
			"filename"     : folderName,
			"path"         : Zarafa.plugins.files.data.Utils.File.stripAccountId(parentFolderId).replace(/\/+$/, ''),
			"id"           : parentFolderId + folderName + "/",
			"virtualRecord": true, // this is important - otherwhise the backend will create another folder
			"message_size" : -1,
			"lastmodified" : d.toUTC().getTime(),
			"type"         : Zarafa.plugins.files.data.FileTypes.FOLDER
		};

		var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(props.id);
		var navpanel = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);
		var currentNode = navpanel.getNodeById(parentFolderId);

		// create the folder in the navbar
		if (Ext.isDefined(currentNode) && currentNode.rendered) {
			currentNode.appendChild(newFolder);
		}

		// add the file to the grid
		if (Zarafa.plugins.files.data.ComponentBox.getStore().getPath() === parentFolderId) {
			var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, data);
			var store = Zarafa.plugins.files.data.ComponentBox.getStore();
			store.add(record);
			store.on("update", Zarafa.plugins.files.data.Actions.doRefreshIconView, Zarafa.plugins.files.data.Actions, {single: true});
			record.commit(true);
		}

		this.dialog.close();
	},

	/**
	 * Event handler which triggered when selection get change in hiererachy tree.
	 *
	 * @param {selectionModel} selectionModel The selectionModel for the treepanel
	 * @param {TreeNode} node The selected tree node
	 * @private
	 */
	onSelectionChange : function(selectionModel, node)
	{
		if (!Ext.isDefined(node)) {
			this.okButton.disable();
			this.cancelButton.disable();
		} else {
			this.okButton.enable();
			this.cancelButton.enable();
			this.selectedFolderId = node.id;
		}
	},

	/**
	 * Event handler triggered when hierarchy tree is loading. function will
	 * selecte the folder from {@link Zarafa.plugins.files.ui.Tree hierarchyTree}
	 * which was selected in {@link Zarafa.plugins.files.data.NavigatorTreeLoader NavigatorTreeLoader}.
	 * @param {TreeNode} node The selected tree node.
	 */
	onTreeNodeLoad : function(node)
	{
		node = this.getNodeFromPath(this.selectedFolderId);
		if (node.id === this.selectedFolderId) {
			this.hierarchyTree.selectNode(node);
			node.expand(false, true , function(){
				this.newNameField.focus(false, 50);
			}, this);
			this.mun(this.hierarchyTree, 'load', this.onTreeNodeLoad, this);
		} else {
			node.expand();
		}
	},


	/**
	 * Helper function which is used to get the rendered node from the hierarchy.
	 *
	 * @param {String} path indecate the selected folder in hierarchy.
	 * @return {TreeNode} node The selected tree node.
	 */
	getNodeFromPath : function (path)
	{
		var node = this.hierarchyTree.getNodeById(path);
		if (!Ext.isDefined(node)) {
			var splitedPathData = path.split('/');
			var slicedPathData = splitedPathData.slice(0,-2);
			var updatedPath = slicedPathData.join('/');
			node = this.getNodeFromPath(updatedPath+"/");
		}
		return node;
	}
});

Ext.reg('filesplugin.createfolderpanel', Zarafa.plugins.files.ui.dialogs.CreateFolderPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.filesrecordcontentpanel
 *
 * This content panel contains the record information panel.
 */
Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param {object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.filesrecordcontentpanel',

			layout: 'fit',
			title : dgettext('plugin_files', 'File information'),
			items : [this.createPanel(config)]
		});

		Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel.superclass.constructor.call(this, config);
	},

	/**
	 * This creates the {@link Zarafa.plugins.files.ui.FilesRecordDetailsPanel}
	 * @param config
	 * @returns {object}
	 */
	createPanel: function (config) {

		return {
			xtype : 'filesplugin.filesrecorddetailspanel',
			record: config.record
		};
	}
});

Ext.reg('filesplugin.filesrecordcontentpanel', Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.filesuploadcontentpanel
 *
 * This class displays the main upload dialog if a users click on the + sign in the tabbar. It will
 * show a simple folder selector tree and a file upload field.
 */
Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @var string The selected destination path
	 */
	targetFolder: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.filesuploadcontentpanel',

			layout: 'fit',
			title : dgettext('plugin_files', 'Upload file'),
			items : [{
				xtype     : 'form',
				ref       : 'mainuploadform',
				layout    : {
					type : 'vbox',
					align: 'stretch',
					pack : 'start'
				},
				fileUpload: true,
				padding   : 5,
				items     : [
					this.createFolderSelector(),
					this.createUploadField()
				],
				buttons   : this.createActionButtons()
			}]
		});

		Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Generates and returns the upload field UI.
	 *
	 * @returns {Object}
	 * @private
	 */
	createUploadField: function () {
		return {
			xtype : 'panel',
			title : dgettext('plugin_files', 'Select a file') + ' (' + dgettext('plugin_files', 'Maximum upload size') + ': ' + Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) + '):',
			layout: 'fit',
			padding: 10,
			items : [{
				xtype     : 'filesplugin.multiplefileuploadfield',
				buttonText: dgettext('plugin_files', 'Browse') + '...',
				name      : 'attachments[]',
				disabled  : true,
				listeners : {
					'fileselected': this.onUploadFieldChanged,
					'scope'       : this
				},
				ref       : '../../mainuploadfield'
			}]
		};
	},

	/**
	 * Generates and returns the folder selector treepanel UI.
	 *
	 * @returns {Object}
	 * @private
	 */
	createFolderSelector: function () {
		return {
			xtype       : 'treepanel',
			anchor      : '0, 0',
			flex        : 1,
			title       : dgettext('plugin_files', 'Select upload folder') + ':',
			root        : {
				nodeType: 'async',
				text    : 'Files',
				id      : '#R#',
				expanded: true,
				cc      : false
			},
			rootVisible : false,
			autoScroll  : true,
			viewConfig  : {
				style: {overflow: 'auto', overflowX: 'hidden'}
			},
			maskDisabled: true,
			listeners   : {
				click     : this.onFolderSelected,
				expandnode: this.onExpandNode,
				scope     : this
			},
			loader      : new Zarafa.plugins.files.data.NavigatorTreeLoader({loadfiles: false})
		};
	},

	/**
	 * The {@link Ext.tree.TreePanel#expandnode} event handler. It will silently load the children of the node.
	 * This is used to check if a node can be expanded or not.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node
	 */
	onExpandNode: function (node) {
		node.attributes["cc"] = true;
		node.eachChild(function (child) {
			if (child.attributes["cc"] !== true) { // only check if it was not checked before
				child.attributes["cc"] = true;
				child.quietLoad();
			}
		});
	},

	/**
	 * Generates and returns the buttons for the dialog.
	 *
	 * @returns {*|Array}
	 */
	createActionButtons: function () {
		return [{
			xtype   : 'button',
			ref     : '../../mainuploadbutton',
			disabled: true,
			text    : '&nbsp;&nbsp;' + dgettext('plugin_files', 'Upload'),
			tooltip : {
				title: dgettext('plugin_files', 'Store selected file'),
				text : dgettext('plugin_files', 'Upload file to the selected folder')
			},
			iconCls : 'icon_files',
			handler : this.doUpload,
			scope   : this
		},
			{
				xtype  : 'button',
				text   : dgettext('plugin_files', 'Cancel'),
				tooltip: {
					title: dgettext('plugin_files', 'Cancel'),
					text : dgettext('plugin_files', 'Close this window')
				},
				handler: this.onClose,
				scope  : this
			}];
	},

	/**
	 * Eventhandler for the onClick event of the treepanel.
	 * The selected folderpath will be stored to this.targetFolder.
	 *
	 * @param folder
	 */
	onFolderSelected: function (folder) {
		this.targetFolder = folder.attributes.id;
		folder.ownerTree.dialog.mainuploadfield.enable();
	},

	/**
	 * Eventhandler for the fileselected event of the filefield.
	 * This function will check the filesize if the browser supports the file API.
	 *
	 * @param field
	 * @param newValue
	 * @param oldValue
	 */
	onUploadFieldChanged: function (field, newValue, oldValue) {
		if (!Ext.isEmpty(newValue)) {
			var form = field.ownerCt.ownerCt.getForm();

			var files;
			files = this.mainuploadfield.fileInput.dom.files;

			var filesTooLarge = false;
			Ext.each(files, function (file) {
				if (file.size > Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) {

					this.mainuploadfield.reset();

					Zarafa.common.dialogs.MessageBox.show({
						title  : dgettext('plugin_files', 'Error'),
						msg    : String.format(dgettext('plugin_files', 'File "{0}" is too large! Maximum allowed filesize: {1}.'), file.name, Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize())),
						icon   : Zarafa.common.dialogs.MessageBox.ERROR,
						buttons: Zarafa.common.dialogs.MessageBox.OK
					});

					this.mainuploadbutton.setDisabled(true);
					filesTooLarge = true;
					return false;
				} else {
					if (!filesTooLarge) {
						this.mainuploadbutton.setDisabled(false);
					}
				}
			}, this);

		} else {
			this.mainuploadbutton.setDisabled(true);
		}
	},

	/**
	 * Eventhandler that will start the upload process.
	 */
	doUpload: function () {
		var form = this.mainuploadfield.ownerCt.ownerCt.getForm();
		var files = this.mainuploadfield.fileInput.dom.files;

		Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, this.targetFolder);
		this.onClose();
	},

	/**
	 * This function will close the dialog.
	 */
	onClose: function () {
		this.close();
	}
});

Ext.reg('filesplugin.filesuploadcontentpanel', Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.savetofilescontentpanel
 *
 * This content panel contains the upload panel for storing files to the backend.
 */
Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			layout     : 'fit',
			title      : dgettext('plugin_files', 'Add item to Files'),
			closeOnSave: true,
			width      : 400,
			height     : 300,

			items: [{
				xtype   : 'filesplugin.savetofilestreepanel',
				ref     : 'treePanel',
				response: config.record
			}]
		});

		Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.savetofilescontentpanel', Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SaveToFilesTreePanel
 * @extends Ext.tree.TreePanel
 * @xtype filesplugin.savetofilestreepanel
 *
 * This dialog panel will provide the filechooser tree for the destination folder selection.
 */
Zarafa.plugins.files.ui.dialogs.SaveToFilesTreePanel = Ext.extend(Ext.tree.TreePanel, {

	/**
	 * @var {String} selectedFolder The folderID of the selected folder
	 */
	selectedFolder: null,

	/**
	 * @var {Object} response holds the response data from the attachment preparation event
	 */
	response: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		this.response = config.response;

		Ext.applyIf(config, {
			pathSeparator : '&',
			root        : {
				nodeType: 'async',
				text    : 'Files',
				id      : '#R#',
				expanded: true,
				cc      : false,
				listeners   : {
					load: this.onAfterRootLoad,
					scope: this
				}
			},
			rootVisible : false,
			autoScroll  : true,
			viewConfig  : {
				style: {overflow: 'auto', overflowX: 'hidden'}
			},
			maskDisabled: true,
			listeners   : {
				click     : this.onTreeNodeClick,
				expandnode: this.onExpandNode,
				scope     : this
			},
			loader      : new Zarafa.plugins.files.data.NavigatorTreeLoader({loadfiles: false}),
			buttons     : [
				this.createActionButtons()
			]
		});
		Zarafa.plugins.files.ui.dialogs.SaveToFilesTreePanel.superclass.constructor.call(this, config);
	},

	/**
	 * Eventhandler for the load event of the root node.
	 * This will be used to expand the path to the previous openend folder.
	 */
	onAfterRootLoad: function() {
		if(Ext.isDefined(Zarafa.plugins.files.current_tree_path)) {
			this.selectedFolder = Zarafa.plugins.files.current_tree_path;
			this.expandPathById(Zarafa.plugins.files.current_tree_path);
		}
	},

	/**
	 * This functions expands the tree until the given id has been reached.
	 * It will automatically load all subnodes if they where not loaded yet.
	 *
	 * @param id
	 * @returns {boolean}
	 */
	expandPathById : function(id) {
		// Take the node id passed in, make a path from the data in the store, then expand that path
		// See if the node is already rendered. If so, just expand it
		var node = this.getNodeById(id);
		var path = '';
		var me = this;

		if (node) {
			path = node.getPath();
			this.expandPath(path, 'id', function(bSuccess, oLastNode) {
				if (bSuccess && me.getNodeById(id)) {
					me.getNodeById(id).ensureVisible(); // scroll to node
					me.getNodeById(id).select(); // select node
				}
			});
			return true;
		} else {
			// The node with that id isnt rendered yet. We need to parse our path:
			// The path will look like this: &part1&part2&part3&part4.....
			// & is the pathSeparator of the treepanel.
			// So a example path will look like this:
			// &#R#&#R#accountid/&#R#accountid/test
			path = '';
			var pathParts = id.replace(/\/+$/,'').split('/'); // split the nodeid

			Ext.each(pathParts, function(pathPart, index) {
				var newpath = this.pathSeparator;
				for(var i=0; i <= index; i++) {
					newpath += pathParts[i] + '/';
				}

				path += newpath;
			}, this);

			path = this.pathSeparator + "#R#" + path; // add root path

			if (path != '') {
				// Expand that path
				this.expandPath(path, 'id', function(bSuccess, oLastNode) {
					if (bSuccess && me.getNodeById(id)) {
						me.getNodeById(id).ensureVisible(); // scroll to node
						me.getNodeById(id).select(); // select node
					}
				});
				return true;
			} else {
				// Couldnt make a path so return false
				return false;
			}
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#expandnode} event handler. It will silently load the children of the node.
	 * This is used to check if a node can be expanded or not.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node
	 * @returns {*}
	 */
	onExpandNode: function (node) {
		if(node.attributes["cc"] !== true) {
			node.attributes["cc"] = true;
			node.eachChild(function (child) {
				if (child.attributes["cc"] !== true) { // only check if it was not checked before
					child.attributes["cc"] = true;
					child.quietLoad();
				}
			});
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#click} event handler. This will set the selectedFolder attribute.
	 *
	 * @param record
	 */
	onTreeNodeClick: function (record) {
		this.selectedFolder = record.attributes.id;

		// save the current path to a global place.
		Zarafa.plugins.files.current_tree_path = this.selectedFolder;
	},

	/**
	 * Generate the toolbar action buttons.
	 *
	 * @returns {Array}
	 */
	createActionButtons: function () {
		return [{
			xtype: 'button',
			text: dgettext('plugin_files', 'New folder'),
			tooltip: {
				title: dgettext('plugin_files', 'New folder'),
				text: dgettext('plugin_files', 'Create a new folder')
			},
			cls: 'zarafa-normal',
			handler: this.newFolder,
			scope: this
		}, {
			xtype: 'button',
			text: dgettext('plugin_files', 'Save'),
			tooltip: {
				title: dgettext('plugin_files', 'Store attachment'),
				text: dgettext('plugin_files', 'Store attachment to the selected Files folder.')
			},
			cls: 'zarafa-action',
			iconCls: 'icon_files_category_white',
			handler: this.uploadFile,
			scope: this
		}];
	},

	/**
	 * This will check if the file already exists on the backend.
	 *
	 * @returns {boolean}
	 */
	uploadFile: function () {
		if (!Ext.isDefined(this.selectedFolder) || Ext.isEmpty(this.selectedFolder)) {
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'You have to choose a folder!'));
		} else {
			// create array to check for dups
			var checkMe = new Array();
			for (var i = 0, len = this.response.count; i < len; i++) {
				checkMe[i] = {
					id      : (this.selectedFolder + this.response.items[i].filename),
					isFolder: false
				};
			}

			try {
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'checkifexists',
					{
						records: checkMe
					},
					new Zarafa.plugins.files.data.ResponseHandler({
						successCallback: this.checkForDuplicateFileDone.createDelegate(this)
					})
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);

				return false;
			}
		}
	},

	/**
	 * This function will prompt the user for a new folder name.
	 */
	newFolder: function () {
		if (!Ext.isDefined(this.selectedFolder) || Ext.isEmpty(this.selectedFolder)) {
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'You have to choose a folder!'));
		} else {
			Zarafa.common.dialogs.MessageBox.prompt(dgettext('plugin_files', 'Folder Name'), dgettext('plugin_files', 'Please enter a foldername'), this.checkForDuplicateFolder, this);
		}
	},

	/**
	 * This function will check if the folder already exists.
	 *
	 * @param button
	 * @param text
	 * @returns {boolean}
	 */
	checkForDuplicateFolder: function (button, text) {
		if (button === "ok") {

			if (!Zarafa.plugins.files.data.Utils.File.isValidFilename(text)) {
				Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'Incorrect foldername'));
			} else {
				try {
					container.getRequest().singleRequest(
						'filesbrowsermodule',
						'checkifexists',
						{
							records: [{id: (this.selectedFolder + text), isFolder: true}]
						},
						new Zarafa.plugins.files.data.ResponseHandler({
							successCallback: this.checkForDuplicateFolderDone.createDelegate(this, [text], true)
						})
					);
				} catch (e) {
					Zarafa.plugins.files.data.Actions.msgWarning(e.message);

					return false;
				}
			}
		}
	},

	/**
	 * This is the callback for the checkduplicate event. If the folder does not exist, it will be created.
	 * Otherwise a warning is shown to the user.
	 *
	 * @param response
	 * @param foldername
	 */
	checkForDuplicateFolderDone: function (response, foldername) {
		if (response.duplicate === false) {
			this.createRemoteFolder(foldername);
		} else {
			Zarafa.plugins.files.data.Actions.msgWarning(dgettext('plugin_files', 'Folder already exists'));
		}
	},

	/**
	 * Callback for the checkduplicate event. If the file does not yet exist it will be uploaded to the server.
	 * Otherwise a warning will be shown.
	 *
	 * @param response
	 */
	checkForDuplicateFileDone: function (response) {
		if (response.duplicate === false) {
			this.doUpload();
		} else {
			Ext.MessageBox.confirm(
				dgettext('plugin_files', 'Confirm overwrite'),
				dgettext('plugin_files', 'File already exists. Do you want to overwrite it?'),
				this.doUpload,
				this
			);
		}
	},

	/**
	 * This function uploads the file to the server.
	 *
	 * @param button
	 */
	doUpload: function (button) {
		if (!Ext.isDefined(button) || button === "yes") {
			try {
				this.disable();
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'uploadtobackend',
					{
						items  : this.response.items,
						count  : this.response.count,
						type   : this.response.type,
						destdir: this.selectedFolder
					},
					new Zarafa.plugins.files.data.ResponseHandler({
						successCallback: this.uploadDone.createDelegate(this)
					})
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		}
	},

	/**
	 * Called after the upload has completed.
	 * It will notify the user and close the upload dialog.
	 *
	 * @param response
	 */
	uploadDone: function (response) {
		if (response.status === true) {
			container.getNotifier().notify('info.files', dgettext('plugin_files', 'Uploaded'), dgettext('plugin_files', 'Attachment successfully stored in Files'));
		} else {
			container.getNotifier().notify('error', dgettext('plugin_files', 'Upload Failed'), dgettext('plugin_files', 'Attachment could not be stored in Files! Error: ' + response.status));
		}

		this.dialog.close();
	},

	/**
	 * This function will create a new folder on the server.
	 *
	 * @param foldername
	 */
	createRemoteFolder: function (foldername) {
		try {
			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'createdir',
				{
					props   : {
						id: this.selectedFolder + foldername
					},
					entryid : this.selectedFolder + foldername,
					parentID: this.selectedFolder
				},
				new Zarafa.plugins.files.data.ResponseHandler({
					successCallback: this.createDirDone.createDelegate(this)
				})
			);
		} catch (e) {
			Zarafa.plugins.files.data.Actions.msgWarning(e.message);
		}
	},

	/**
	 * Called after the folder has been created. It will reload the filetree.
	 *
	 * @param response
	 */
	createDirDone: function (response) {
		container.getNotifier().notify('info.files', dgettext('plugin_files', 'Created'), dgettext('plugin_files', 'Directory created!'));

		var parent = response.item[0].parent_entryid;
		var node = this.getNodeById(parent);

		if (Ext.isDefined(node)) {
			if (!node.isLeaf()) {
				node.reload();
			} else {
				var currentfolder = (parent.substr(-1) == '/') ? parent.substr(0, parent.length - 1) : parent;
				var parentnode = this.getNodeById(currentfolder.match(/.*\//));
				if (Ext.isDefined(parentnode)) {
					parentnode.on("expand", this.reloadParentDone.createDelegate(this, [parent]), this, {single: true});
					parentnode.reload();
				}
			}
		}
	},

	/**
	 * Callback for the expand event. It will expand the subnode.
	 *
	 * @param subnode
	 */
	reloadParentDone: function (subnode) {
		var node = this.getNodeById(subnode);

		if (Ext.isDefined(node)) {
			node.expand();
		}
	}
});

Ext.reg('filesplugin.savetofilestreepanel', Zarafa.plugins.files.ui.dialogs.SaveToFilesTreePanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.ShareContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.sharecontentpanel
 *
 * This content panel contains the sharing panel.
 */
Zarafa.plugins.files.ui.dialogs.ShareContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @var {Array} records
	 */
	records: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout     : 'fit',
			title      : dgettext('plugin_files', 'Share Files'),
			closeOnSave: true,
			width      : 800,
			height     : 445,

			items: [
				container.populateInsertionPoint('plugin.files.sharedialog', this, config.context)
			]
		});

		Zarafa.plugins.files.ui.dialogs.ShareContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.sharecontentpanel', Zarafa.plugins.files.ui.dialogs.ShareContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SharePanel
 * @extends Ext.Panel
 * @xtype filesplugin.sharepanel
 *
 * This panel must be extended! It will then decide if it should be displayed or not.
 * Make sure that the plugin has a name set! The name should be in the following format:
 * filesbackend<Backendname>
 *
 * e.g.: filesbackendSMB, filesbackendOwncloud....
 */
Zarafa.plugins.files.ui.dialogs.SharePanel = Ext.extend(Ext.Panel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		var records = config.ownerCt.records;
		var hidden = true;


		// check if this panel should be enabled
		if (records.length > 0) {
			var account = records[0].getAccount();
			var backend = account.get("backend");
			var backendPlugin = config.plugin.info.name;

			// "filesbackend" has 12 chars
			if (backend === backendPlugin.substring(12)) {
				hidden = false;
			}
		}

		if (hidden) {
			Ext.applyIf(config, {
				disabled: true,
				hidden  : true
			});
		}


		Zarafa.plugins.files.ui.dialogs.SharePanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.sharepanel', Zarafa.plugins.files.ui.dialogs.SharePanel);Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.uploadstatuscontentpanel
 *
 * The content panel for the main upload status panel.
 */
Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout : 'fit',
			title : dgettext('plugin_files', 'Uploading files to ') + Zarafa.plugins.files.data.Utils.File.stripAccountId(config.destination) + ' &hellip;',
			closeOnSave : true,
			width : 480,
			height : 445,
			items: [{
				xtype : 'filesplugin.uploadstatuspanel',
				files : config.files,
				destination : config.destination,
				keepBoth : config.keepBoth,
				callbackAllDone : config.callbackAllDone || Ext.emptyFn,
				callbackUploadFailed : config.callbackUploadFailed || Ext.emptyFn,
				callbackUploadAborted : config.callbackUploadAborted || Ext.emptyFn
			}]
		});

		Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.uploadstatuscontentpanel', Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.UploadStatusPanel
 * @extends Ext.form.FormPanel
 * @xtype filesplugin.uploadstatuspanel
 *
 * This panel will upload files via ajax and display some nice progressbars.
 */
Zarafa.plugins.files.ui.dialogs.UploadStatusPanel = Ext.extend(Ext.form.FormPanel, {

	/**
	 * @var FileList or string[]
	 */
	files: null,

	/**
	 * @var string
	 */
	destination: null,

	/**
	 * @var XMLHttpRequest[]
	 */
	xhr: [],

	/**
	 * @var object Timer object for calculating the upload speed
	 */
	statsTimer: null,

	/**
	 * @var int Lock for synchronisation between timer runs
	 */
	timerCounter: null,

	/**
	 * @var int Timerinterval in milliseconds
	 */
	timerIntervall: 200,

	/**
	 * @var function Callback gets called if all files were uploaded
	 */
	callbackAllDone: Ext.emptyFn,

	/**
	 * @var function Callback gets called if one upload fails
	 */
	callbackUploadFailed: Ext.emptyFn,

	/**
	 * @var function Callback gets called if one upload was aborted
	 */
	callbackUploadAborted: Ext.emptyFn,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			autoScroll : true,
			defaults : {
				anchor: '100%'
			},
			items    : this.initUploadUI(config),
			listeners: {
				afterrender: this.startUpload,
				scope : this
			}
		});

		Zarafa.plugins.files.ui.dialogs.UploadStatusPanel.superclass.constructor.call(this, config);
	},

	/**
	 * This function generates a UI element that displays the file upload stats (ETA, speed, size...)
	 * @param file
	 * @param ref
	 * @returns {object}
	 */
	getBasicElement: function (file, ref) {
		var filename = file.name;
		var filesize = file.size;

		return {
			xtype        : 'panel',
			padding      : 10,
			custom_fileid: ref,
			ref          : 'fileuploadfield_' + ref,
			items        : [{
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .8,
					items      : {
						xtype     : 'displayfield',
						fieldClass: 'fp_upload_header',
						value     : filename
					}
				}, {
					columnWidth: .2,
					style      : 'text-align: right;',
					items      : {
						xtype     : 'displayfield',
						fieldClass: 'fp_upload_header',
						value     : Zarafa.plugins.files.data.Utils.Format.fileSizeList(filesize)
					}
				}]
			}, {
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .95,
					items      : {
						xtype: 'progress',
						text : dgettext('plugin_files', 'Starting upload') + '&hellip;',
						ref  : '../../progress'
					}
				}, {
					columnWidth: .05,
					items      : {
						xtype        : 'button',
						ref          : '../../cancel',
						custom_fileid: ref,
						tooltip      : {
							title: dgettext('plugin_files', 'Cancel'),
							text : dgettext('plugin_files', 'Cancel upload')
						},
						overflowText : dgettext('plugin_files', 'Cancel upload'),
						iconCls      : 'icon_action_cancel',
						handler      : this.doCancelUpload.createDelegate(this)
					}
				}]
			}, {
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .33,
					items      : {
						xtype: 'displayfield',
						value: '- kB/s',
						ref  : '../../speed'
					}
				}, {
					columnWidth: .33,
					items      : [{
						xtype: 'displayfield',
						value: '0 MB',
						ref  : '../../uploaded'
					}]
				}, {
					columnWidth: .33,
					style      : 'text-align: right;',
					items      : {
						xtype: 'displayfield',
						value: dgettext('plugin_files', '- seconds left'),
						ref  : '../../eta'
					}
				}]
			}]
		};
	},

	/**
	 * Generates one UI element for each file
	 *
	 * @param config
	 * @returns {Array}
	 */
	initUploadUI: function (config) {
		var files = config.files;

		var items = [];

		Ext.each(files, function (file, index) {
			items.push(this.getBasicElement(file, index));
		}, this);

		return items;
	},

	/**
	 * This function generates the XMLHttpRequest's and starts the upload.
	 * The upload timer gets also started in this function.
	 */
	startUpload: function ()
	{
		Ext.each(this.files, function (file, index) {
			this.xhr[index] = new XMLHttpRequest();
			this.xhr[index].open("post", "index.php?load=custom&name=upload_file&keep_both="+this.keepBoth, true);

			// progress listener
			this.xhr[index].upload.addEventListener("progress", this.onUpdateProgress.createDelegate(this, [index], true), false);

			// finish listener
			this.xhr[index].addEventListener("load", this.onFinishProgress.createDelegate(this, [index], true), false);

			// error listener
			this.xhr[index].addEventListener("error", this.onUploadFailed.createDelegate(this, [index], true), false);

			// abort listener
			this.xhr[index].addEventListener("abort", this.onUploadAborted.createDelegate(this, [index], true), false);

			// Set headers - important for the php backend!
			this.xhr[index].setRequestHeader("Content-Type", "multipart/form-data");
			this.xhr[index].setRequestHeader("X-FILE-NAME", file.name);
			this.xhr[index].setRequestHeader("X-FILE-SIZE", file.size);
			this.xhr[index].setRequestHeader("X-FILE-TYPE", file.type);
			this.xhr[index].setRequestHeader("X-FILE-DESTINATION", this.destination);

			// Send the file
			this.xhr[index].send(file);

			this.xhr[index].cust_loaded = 0; // store loaded and total size to xhr element
			this.xhr[index].cust_total = 0;
		}, this);

		this.statsTimer = window.setInterval(this.getStats.createDelegate(this), this.timerIntervall);
		this.timerCounter = this.xhr.length; // set count for locking
	},

	/**
	 * This function gets called by the upload timer. It calculates upload speed
	 * and the remaining time.
	 */
	getStats: function () {
		Ext.each(this.xhr, function (request, index) {
			var oldloaded = request.cust_loaded_old;
			var loaded = request.cust_loaded;
			var total = request.cust_total;
			request.cust_loaded_old = loaded;

			// calculate speed and eta
			var speed = (loaded - oldloaded) / (this.timerIntervall / 1000); // bytes per second
			var speed_unit = ' B/s';

			// calc ETA
			var eta = (total - loaded) / speed; // seconds
			var eta_unit = dgettext('plugin_files', ' seconds left');

			if (eta > 60) {
				eta = eta / 60; // minutes
				eta_unit = dgettext('plugin_files', ' minutes left');
			}

			// transform speed units
			if (speed > 1000) {
				speed = speed / 1000; // kBps
				speed_unit = ' kB/s';
			}
			if (speed > 1000) {
				speed = speed / 1000; // mBps
				speed_unit = ' mB/s';
			}


			var filesUploaderPanel = this["fileuploadfield_" + index];
			if (Ext.isDefined(filesUploaderPanel)) {
				if (Ext.isDefined(oldloaded) && loaded != 0 && total != 0) {
					if (loaded != oldloaded) {
						filesUploaderPanel.speed.setValue(speed.toFixed(2) + speed_unit);
						filesUploaderPanel.eta.setValue(parseInt(eta) + eta_unit);
					}
				} else {
					filesUploaderPanel.speed.setValue('- kB/s');
					filesUploaderPanel.eta.setValue(dgettext('plugin_files', '- seconds left'));
				}
			}
		}, this);
	},

	/**
	 * Callback for the 'progress' event of the XHR request.
	 * It will update the progressbar and set the uploaded filesize.
	 *
	 * @param event
	 * @param index
	 */
	onUpdateProgress: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];

		if (Ext.isDefined(filesUploaderPanel)) {
			if (event.lengthComputable) {
				this.xhr[index].cust_loaded = event.loaded; // store loaded and total size to xhr element
				this.xhr[index].cust_total = event.total;

				var finished = ((event.loaded / event.total) * 100).toFixed(2);
				filesUploaderPanel.progress.updateProgress((event.loaded / event.total), dgettext('plugin_files', 'Uploading: ') + finished + '%', true);
				filesUploaderPanel.uploaded.setValue(Zarafa.plugins.files.data.Utils.Format.fileSizeList(event.loaded));
			} else {
				filesUploaderPanel.progress.updateProgress(0.5, dgettext('plugin_files', 'Upload status unavailable... please wait.'), true);
			}
		}
	},

	/**
	 * Callback for the 'load' event of the XHR request.
	 * This callback gets called after the file was uploaded to the server.
	 * It will update the progressbar and reset the uploaded filesize.
	 *
	 * @param event
	 * @param index
	 */
	onFinishProgress: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		// If files upload panel is not already closed then do go farther operations like
		// disable the cancel button and update progress bar etc.
		if (filesUploaderPanel) {
			filesUploaderPanel.progress.updateProgress(1, dgettext('plugin_files', 'Upload finished!'), true);

			// reset stats - to tell the timer to stop
			this.xhr[index].cust_loaded = 0; // store loaded and total size to xhr element
			this.xhr[index].cust_total = 0;

			filesUploaderPanel.cancel.disable();
			this.checkTimerAlive();
		}
	},

	/**
	 * Callback for the 'abort' event of the XHR request.
	 * This callback gets called after a upload was aborted by the user.
	 * It will update the progressbar.
	 *
	 * @param event
	 * @param index
	 */
	onUploadAborted: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		if (Ext.isDefined(filesUploaderPanel)) {
			var progressbar = filesUploaderPanel.progress;
			progressbar.updateProgress(1, dgettext('plugin_files', 'Upload aborted!'), true);
			progressbar.addClass("fp_upload_canceled");
			filesUploaderPanel.cancel.disable();

			// call callback
			this.callbackUploadAborted(this.files[index], this.destination, event);
			this.checkTimerAlive();
		}
	},

	/**
	 * Callback for the 'error' event of the XHR request.
	 * This callback gets called after a upload failed.
	 * It will update the progressbar.
	 *
	 * @param event
	 * @param index
	 */
	onUploadFailed: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		if (Ext.isDefined(filesUploaderPanel)) {
			var progressbar = filesUploaderPanel.progress;
			progressbar.updateProgress(1, dgettext('plugin_files', 'Upload failed!'), true);
			progressbar.addClass("fp_upload_canceled");
			filesUploaderPanel.cancel.disable();

			// call callback
			this.callbackUploadFailed(this.files[index], this.destination, event);
			this.checkTimerAlive();
		}
	},

	/**
	 * This function will decrease the timer counter lock.
	 * If the lock is zero the dialog will be closed.
	 */
	checkTimerAlive: function () {
		this.timerCounter--;
		if (this.timerCounter <= 0) {
			window.clearTimeout(this.statsTimer);
			this.onUploadsFinished();
		}
	},

	/**
	 * This function can abort one XHR request.
	 *
	 * @param button
	 * @param event
	 */
	doCancelUpload: function (button, event) {
		this.xhr[button.custom_fileid].abort();
	},

	/**
	 * Close the dialog.
	 */
	onUploadsFinished: function () {
		this.dialog.close();
		this.callbackAllDone(this.files, this.destination, this.xhr);
	}
});

Ext.reg('filesplugin.uploadstatuspanel', Zarafa.plugins.files.ui.dialogs.UploadStatusPanel);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

/**
 * @class Zarafa.plugins.files.ui.snippets.FilesNavigationBar
 * @extends Ext.Panel
 * @xtype filesplugin.navigationbar
 *
 * This panel will display a windows explorer like navigation bar.
 */
Zarafa.plugins.files.ui.snippets.FilesNavigationBar = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.core.ContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.mail.MailContextModel
	 */
	model: undefined,

	/**
	 * Maximum path parts to show. If the folder path is deeper,
	 * a back arrow will be shown.
	 * @property
	 * @type {Number}
	 */
	maxPathBeforeTruncate: 5,

	/**
	 * @cfg {Number} maxStringBeforeTruncate Maximum stringlength of a folder before it will get truncated.
	 */
	maxStringBeforeTruncate: 20,

	/**
	 * @cfg {String} pathTruncateText String that will be displayed in the back button.
	 */
	pathTruncateText: "&hellip;",

	/**
	 * Overflow flag. If this flag is true, the overflow button will be shown.
	 * @property
	 * @type boolean
	 */
	hasOverflow: false,

	/**
	 * The current path.
	 *
	 * @property
	 * @type {String}
	 */
	currentPath: '#R#',

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			xtype       : 'filesplugin.navigationbar',
			maskDisabled: false,
			hideBorders : true,
			border      : false,
			cls: 'navbar_container',
			height: 25,
			defaults: {
				height: 25
			},
			layout      : 'column',
			items       : []
		});

		Zarafa.plugins.files.ui.snippets.FilesNavigationBar.superclass.constructor.call(this, config);
	},

	/**
	 * initializes the events.
	 * @private
	 */
	initEvents: function () {
		var filesStore = Zarafa.plugins.files.data.ComponentBox.getStore();

		this.mon(filesStore, {
			'beforeload': this.onStoreLoad,
			scope       : this
		});

		// do the inital check after rendering
		this.currentPath = filesStore.getPath();
		this.on('afterrender', this.generateNavigationButtons.createDelegate(
			this,
			[
				Zarafa.plugins.files.data.Utils.File.stripAccountId(this.currentPath),
				Zarafa.plugins.files.data.Utils.File.getAccountId(this.currentPath)
			]
		), this, {single: true});
	},

	/**
	 * Event handler which will be called when the {@link #store} fires the
	 * {@link Zarafa.plugins.files.data.FilesRecordStore#beforeload} event.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store the files store.
	 * @param {Array} records
	 * @param {Object} options
	 * @private
	 */
	onStoreLoad: function (store, records, options) {
		var currentPath = store.getPath();
		var accID = Zarafa.plugins.files.data.Utils.File.getAccountId(currentPath);
		var backendPath = Zarafa.plugins.files.data.Utils.File.stripAccountId(currentPath);
		if(this.currentPath != currentPath) {
			this.currentPath = currentPath;
			this.generateNavigationButtons(backendPath, accID);
		}
	},

	/**
	 * Calculate the maximum number of folders we can display.
	 * @private
	 */
	recalculateMaxPath: function() {
		var maxItemWidth = this.maxStringBeforeTruncate * 8; // one char is 8 pixels long
		var totalWidth = this.getWidth();
		var children = this.items ? this.items.items : [];
		var removeStatic = this.hasOverflow ? 3 : 2; // -2 because home and account folder does not count

		Ext.each(children, function (child) {
			totalWidth -= child.getWidth();
		});

		if(totalWidth < maxItemWidth) {
			// we need more space - remove first children
			// so we need at least maxItemWidth - totalWidth;

			var spaceNeeded = maxItemWidth - totalWidth;
			var childrenToRemove = 0;

			Ext.each(children, function (child) {
				spaceNeeded -= child.getWidth();
				childrenToRemove++;

				if(spaceNeeded <= 0) {
					return false;
				}
			});

			this.maxPathBeforeTruncate = children.length - childrenToRemove;
		} else {
			this.maxPathBeforeTruncate = Math.floor(children.length + (totalWidth / maxItemWidth));
		}
		this.maxPathBeforeTruncate = this.maxPathBeforeTruncate - removeStatic;
	},

	/**
	 * Create buttons for the given folder path.
	 *
	 * @param path
	 * @param accountID
	 */
	generateNavigationButtons: function (path, accountID) {

		// recalculate the width
		this.recalculateMaxPath();

		// first remove old buttons
		this.removeAll(true);

		var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		// look up the account
		var account = null;

		if (!Ext.isEmpty(accountID)) {
			account = accStore.getById(accountID);
		}

		var homeButton = new Ext.Button({
			cls: "files_navbar_button files_navbar_button_first",
			path: "#R#", // root folder
			listeners: {
				click: this.doNavButtonClick
			},
			iconCls: "files_navbar files_navbar_home"
		});
		this.add(homeButton);

		if (!Ext.isEmpty(accountID) && Ext.isDefined(account)) {
			var lastCls = (path.indexOf("/") === -1 || path === "/") ? " files_navbar_button_last" : "";
			var accountName = Zarafa.plugins.files.data.Utils.Format.truncate(account.get("name"), this.maxStringBeforeTruncate);
			var accButton = new Ext.Button({
				cls: "files_navbar_button" + lastCls,
				path: "#R#" + accountID + "/", // backend root folder
				listeners: {
					click: this.doNavButtonClick
				},
				text   : accountName
			});

			// If account name is not set by user then show account backend icon
			if (accountName === '') {
				accButton.setIconClass("files_navbar icon_16_" + account.get("backend"));
			}
			this.add(accButton);
		}

		if (path.indexOf("/") !== -1 && path !== "/") {
			var currPath = "/";
			var pathParts = path.replace(/^\/|\/$/g, '').split("/"); // trim leading and trailing slash and split afterwards

			if(pathParts.length > this.maxPathBeforeTruncate) {
				this.hasOverflow = true;

				var overflowParts = pathParts.splice(0, (pathParts.length - this.maxPathBeforeTruncate));

				var menu = [];
				Ext.each(overflowParts, function (pathPart) {
					currPath += pathPart + "/";
					menu.push({
						text: Zarafa.plugins.files.data.Utils.Format.truncate(pathPart, this.maxStringBeforeTruncate),
						handler: this.doNavButtonClick,
						iconCls: 'icon_folder_note',
						path: "#R#" + accountID + currPath
					});
				}, this);

				var overflowButton = new Ext.Button({
					cls: "files_navbar_button",
					menu: menu,
					text   : this.pathTruncateText
				});
				this.add(overflowButton);
			} else {
				this.hasOverflow = false;
			}

			Ext.each(pathParts, function (pathPart, index) {
				currPath += pathPart + "/";
				var lastCls = index == (pathParts.length-1) ? " files_navbar_button_last" : "";
				var navBtn = new Ext.Button({
					text: Zarafa.plugins.files.data.Utils.Format.truncate(pathPart, this.maxStringBeforeTruncate),
					cls: "files_navbar_button" + lastCls,
					path: "#R#" + accountID + currPath,
					listeners: {
						click: this.doNavButtonClick
					}
				});
				this.add(navBtn);
			}, this);
		}

		this.doLayout();
	},

	/**
	 * Eventhandler that handles a navigation button click.
	 *
	 * @param button
	 * @param event
	 */
	doNavButtonClick: function(button, event) {
		// read the path property
		var path = button.path;

		// reload main store
		Zarafa.plugins.files.data.ComponentBox.getStore().loadPath(path);

		// reselect navigator tree
		var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(path);
		var nav = Zarafa.plugins.files.data.ComponentBox.getNavigatorTreePanel(accountID);

		if(nav) {
			var n = nav.getNodeById(path);
			if (Ext.isDefined(n)) {
				n.select();
			}
		}
	}
});

Ext.reg('filesplugin.navigationbar', Zarafa.plugins.files.ui.snippets.FilesNavigationBar);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.FilesQuotaBar = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.core.ContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.mail.MailContextModel
	 */
	model: undefined,

	/**
	 * @cfg String
	 */
	quotaText: dgettext('plugin_files', '{0} of {1} in use'), // String.format(this.pageInfoText, pageData, total)

	/**
	 * @cfg Boolean
	 */
	loadOnlyOnce: true,

	/**
	 * @cfg String
	 */
	defaultDirectory: "/",

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			xtype       : 'filesplugin.quotabar',
			cls         : 'files_quota_bar_snippet',
			maskDisabled: false,
			hideBorders : true,
			border      : false,
			items       : [{
				xtype       : 'panel',
				ref         : 'quotaPanel',
				layout      : 'table',
				layoutConfig: {columns: 2},
				cls         : 'files_quota_bar_container',
				maskDisabled: false,
				hideBorders : true,
				hidden      : true,
				border      : false,
				defaults    : {
					frame: false
				},
				items       : [{
					xtype: 'label',
					ref  : '../usageInfo',
					autoWidth: true
				}, {
					xtype: 'progress',
					width: '150',
					ref  : '../progressBar',
					height: '8',
					cls  : 'files_quota_bar',
					style: 'margin: 0 0 0 20px'
				}]
			}, {
				xtype: 'label',
				border      : false,
				hideBorders : true,
				maskDisabled: false,
				ref  : 'loadingIcon',
				html : '<div class="img"></div>'
			}]
		});

		Zarafa.plugins.files.ui.snippets.FilesQuotaBar.superclass.constructor.call(this, config);
		this.initEvents();
	},

	/**
	 * initializes the events.
	 * @private
	 */
	initEvents: function () {
		var filesStore = Zarafa.plugins.files.data.ComponentBox.getStore();

		// do the initial check
		this.onStoreLoad(filesStore);

		this.mon(filesStore, {
			'beforeload': this.onStoreLoad,
			scope : this
		});
	},

	/**
	 * Event handler which will be called when the {@link #model} fires the
	 * {@link Zarafa.core.ContextModel#folderchange} event. This will determine
	 * if the selected folders support 'search folders' and update the UI accordingly.
	 * @param {Zarafa.core.ContextModel} model this context model.
	 * @param {Array} folders selected folders as an array of {Zarafa.hierarchy.data.MAPIFolderRecord Folder} objects.
	 * @private
	 */
	onStoreLoad: function (store, records, options) {
		var nodeID = store.getPath();
		var accID = Zarafa.plugins.files.data.Utils.File.getAccountId(nodeID);

		var accStore = Zarafa.plugins.files.data.singleton.AccountStore.getStore();

		// look up the account
		var account = accStore.getById(accID);

		if (Ext.isDefined(account) && account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.QUOTA)) {

			// load the information only once or if a new account was loaded.
			if ((!Ext.isDefined(this.loaded) || this.loaded != accID) || !this.loadOnlyOnce) {

				// ui updates
				this.show();
				this.quotaPanel.hide();
				this.loadingIcon.show();

				this.loadQuotaInformation(accID, this.defaultDirectory);
			}

			// set the loaded flag to true
			this.loaded = accID;
		} else {
			this.hide(); // hide the complete component
		}
	},

	/**
	 * Request quota values from the server.
	 */
	loadQuotaInformation: function (accountID, directory) {
		var responseHandler = new Zarafa.plugins.files.data.ResponseHandler({
			successCallback: this.gotQuotaValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getquota',
			{
				accountId: accountID,
				folder   : directory
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It will update the textfield values.
	 *
	 * @param response
	 */
	gotQuotaValues: function (response) {
		if (!this.quotaPanel) {
			return;
		}

		var used = parseInt(response["quota"][0].amount);
		var free = parseInt(response["quota"][1].amount);
		var total = used + free;

		// show/hide components
		this.loadingIcon.hide();
		this.quotaPanel.show();

		// Update text values
		this.usageInfo.setText(String.format(this.quotaText, Ext.util.Format.fileSize(used), Ext.util.Format.fileSize(total)));

		// Update progressbar
		this.progressBar.updateProgress(used / total);


	}
});

Ext.reg('filesplugin.quotabar', Zarafa.plugins.files.ui.snippets.FilesQuotaBar);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.PDFjsPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg{String} src
	 * URL to the PDF - Same Domain or Server with CORS Support
	 */
	src: '',

	/**
	 * @cfg {String} title The title of the pdf document.
	 */
	title: '',

	/**
	 * @cfg{Double} pageScale
	 * Initial scaling of the PDF. 1 = 100%
	 */
	pageScale: 1,

	/**
	 * @cfg{Boolean} disableWorker
	 * Disable workers to avoid yet another cross-origin issue(workers need the URL of
	 * the script to be loaded, and currently do not allow cross-origin scripts)
	 */
	disableWorker: true,

	/**
	 * @cfg{Boolean} disableTextLayer
	 * Enable to render selectable but hidden text layer on top of an PDF-Page.
	 * This feature is buggy by now and needs more investigation!
	 */
	disableTextLayer: true, // true by now, cause it??s buggy

	/**
	 * @cfg{String} loadingMessage
	 * The text displayed when loading the PDF.
	 */
	loadingMessage: 'Loading PDF, please wait...',

	/**
	 * @cfg{String} beforePageText
	 * The text displayed before the input item.
	 */
	beforePageText: 'Page',

	/**
	 * @cfg{String} afterPageText
	 * Customizable piece of the default paging text. Note that this string is formatted using
	 *{0} as a token that is replaced by the number of total pages. This token should be preserved when overriding this
	 * string if showing the total page count is desired.
	 */
	afterPageText: 'of {0}',

	/**
	 * @cfg{String} firstText
	 * The quicktip text displayed for the first page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	firstText: 'First Page',

	/**
	 * @cfg{String} prevText
	 * The quicktip text displayed for the previous page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	prevText: 'Previous Page',

	/**
	 * @cfg{String} nextText
	 * The quicktip text displayed for the next page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	nextText: 'Next Page',

	/**
	 * @cfg{String} lastText
	 * The quicktip text displayed for the last page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	lastText: 'Last Page',

	/**
	 * @cfg{String} fullscreenText
	 * The quicktip text displayed for the fullscreen button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	fullscreenText: 'Fullscreen',

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the input field used to display and change the current page number.
	 */
	inputItemWidth: 30,

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the combobox used to change display scale of the PDF.
	 */
	scaleWidth: 60,

	/**
	 * @constructor
	 */
	constructor: function (config) {
		config = config || {};

		config = Ext.applyIf(config, {
			xtype     : 'filesplugin.pdfjspanel',
			border    : false,
			baseCls   : 'x-panel pdf',
			bodyCssCls: 'pdf-body',
			pageScale : 1,
			header    : false, // hide title
			items     : [{
				xtype    : 'component',
				autoEl   : {
					cn: [
						{tag: 'canvas', 'class': 'pdf-page-container'},
						{tag: 'div', 'class': 'pdf-text-layer'}
					]
				},
				listeners: {
					afterrender: this.init.createDelegate(this)
				}
			}],
			bbar      : this.getPagingToolbar()
		});

		PDFJS.disableTextLayer = this.disableTextLayer;

		Zarafa.plugins.files.ui.snippets.PDFjsPanel.superclass.constructor.call(this, config);
	},

	getPagingToolbar: function () {
		var me = this;

		return {
			xtype : 'toolbar',
			height: 25,
			items : [{
				ref         : 'first',
				tooltip     : me.firstText,
				overflowText: me.firstText,
				iconCls     : 'x-tbar-page-first',
				disabled    : true,
				handler     : me.moveFirst,
				scope       : me
			}, {
				ref         : 'prev',
				tooltip     : me.prevText,
				overflowText: me.prevText,
				iconCls     : 'x-tbar-page-prev',
				disabled    : true,
				handler     : me.movePrevious,
				scope       : me
			}, '-', me.beforePageText, {
				xtype          : 'numberfield',
				ref            : 'inputItem',
				name           : 'inputItem',
				cls            : 'x-tbar-page-number',
				minValue       : 1,
				allowDecimals  : false,
				allowNegative  : false,
				enableKeyEvents: true,
				selectOnFocus  : true,
				submitValue    : false,
				width          : me.inputItemWidth,
				disabled       : true,
				margins        : '-1 2 3 2',
				listeners      : {
					scope  : me,
					keydown: me.onPagingKeyDown,
					blur   : me.onPagingBlur
				}
			}, {
				xtype  : 'tbtext',
				ref    : 'afterTextItem',
				text   : String.format(me.afterPageText, 1),
				margins: '0 5 0 0'
			}, '-', {
				ref         : 'next',
				tooltip     : me.nextText,
				overflowText: me.nextText,
				iconCls     : 'x-tbar-page-next',
				disabled    : true,
				handler     : me.moveNext,
				scope       : me
			}, {
				ref         : 'last',
				tooltip     : me.lastText,
				overflowText: me.lastText,
				iconCls     : 'x-tbar-page-last',
				disabled    : true,
				handler     : me.moveLast,
				scope       : me
			}, '->', {
				xtype         : 'combo',
				ref           : 'scaleCombo',
				triggerAction : 'all',
				lazyInit      : false,
				forceSelection: true,
				editable      : false,
				autoSelect    : true,
				disabled      : true,
				hidden        : true, // dont show this element . (for now... TODO)
				width         : me.scaleWidth,
				store         : {
					xtype      : 'jsonstore',
					autoDestroy: true,
					fields     : ['scale', 'text'],
					data       : [
						{
							scale: 0.5,
							text : '50%'
						}, {
							scale: 0.75,
							text : '75%'
						}, {
							scale: 1,
							text : '100%'
						}, {
							scale: 1.25,
							text : '125%'
						}, {
							scale: 1.5,
							text : '150%'
						}, {
							scale: 2,
							text : '200%'
						}, {
							scale: 4,
							text : '400%'
						}
					]
				},
				valueField    : 'scale',
				displayField  : 'text',
				mode          : 'local',
				listeners     : {
					scope : me,
					select: me.onScaleChange
				}
			}, {
				ref         : 'fullscreen',
				tooltip     : me.fullscreenText,
				overflowText: me.fullscreenText,
				iconCls     : 'files_icon_action_fullscreen',
				disabled    : false,
				handler     : me.displayFullscreen,
				scope       : me
			}]
		};
	},

	init: function () {
		// init gui elements
		this.pageContainer = this.el.query('.pdf-page-container')[0];

		if (!PDFJS.disableTextLayer) {
			this.textLayerDiv = this.el.query('.pdf-text-layer')[0];
		}

		if (this.disableWorker) {
			PDFJS.disableWorker = true;
		}

		// Asynchronously download PDF as an ArrayBuffer
		this.getDocument();
	}
	,

	onLoad: function () {
		try {
			var me = this, isEmpty;

			isEmpty = me.pdfDoc.numPages === 0;
			me.currentPage = me.currentPage || (isEmpty ? 0 : 1);

			me.renderPage(me.currentPage);
		}
		catch (error) {
			console.log("PDF: Can't render: " + error.message);
		}
	}
	,

	renderPage: function (num) {
		var me = this;
		var isEmpty, pageCount, currPage, afterText;
		var toolbar = me.getBottomToolbar();

		if (this.isRendering) {
			return;
		}

		me.isRendering = true;
		me.currentPage = num;

		currPage = me.currentPage;
		pageCount = me.pdfDoc.numPages;
		afterText = String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
		isEmpty = pageCount === 0;

		toolbar.afterTextItem.setText(afterText);
		toolbar.inputItem.setDisabled(isEmpty);
		toolbar.inputItem.setValue(currPage);
		toolbar.first.setDisabled(currPage === 1 || isEmpty);
		toolbar.prev.setDisabled(currPage === 1 || isEmpty);
		toolbar.next.setDisabled(currPage === pageCount || isEmpty);
		toolbar.last.setDisabled(currPage === pageCount || isEmpty);
		toolbar.scaleCombo.setDisabled(isEmpty);
		toolbar.scaleCombo.setValue(me.pageScale);

		// Using promise to fetch the page
		me.pdfDoc.getPage(num).then(function (page) {

			if (!me.pageContainer) {
				// pageContainer not available. Widget destroyed already?
				me.isRendering = false;

				return;
			}

			var viewport = page.getViewport(me.pageScale);
			me.pageContainer.height = viewport.height;
			me.pageContainer.width = viewport.width;

			var ctx = me.pageContainer.getContext('2d');
			ctx.save();
			ctx.fillStyle = 'rgb(255, 255, 255)';
			ctx.fillRect(0, 0, me.pageContainer.width, me.pageContainer.height);
			ctx.restore();

			// see https://github.com/SunboX/ext_ux_pdf_panel/blob/master/ux/panel/PDF.js
			var textLayer = null; // TODO: this should be implemented...

			// Render PDF page into canvas context
			var renderContext = {
				canvasContext: ctx,
				viewport     : viewport,
				textLayer    : textLayer
			};
			page.render(renderContext);

			me.isRendering = false;

			if (me.loader) {
				me.loader.destroy();
			}

			if (me.rendered) {
				me.fireEvent('change', me, {
					current: me.currentPage,
					total  : me.pdfDoc.numPages
				});
			}
		});
	},

	moveFirst: function () {
		var me = this;
		if (me.fireEvent('beforechange', me, 1) !== false) {
			me.renderPage(1);
		}
	},

	movePrevious: function () {
		var me = this,
			prev = me.currentPage - 1;

		if (prev > 0) {
			if (me.fireEvent('beforechange', me, prev) !== false) {
				me.renderPage(prev);
			}
		}
	},

	moveNext: function () {
		var me = this,
			total = me.pdfDoc.numPages,
			next = me.currentPage + 1;

		if (next <= total) {
			if (me.fireEvent('beforechange', me, next) !== false) {
				me.renderPage(next);
			}
		}
	},

	moveLast: function () {
		var me = this,
			last = me.pdfDoc.numPages;

		if (me.fireEvent('beforechange', me, last) !== false) {
			me.renderPage(last);
		}
	},

	readPageFromInput: function () {
		var me = this, v = me.getBottomToolbar().inputItem.getValue(),
			pageNum = parseInt(v, 10);

		if (!v || isNaN(pageNum)) {
			me.getBottomToolbar().inputItem.setValue(me.currentPage);
			return false;
		}
		return pageNum;
	},

	onPagingFocus: function () {
		this.getBottomToolbar().inputItem.select();
	},

	onPagingBlur: function (e) {
		var curPage = this.getPageData().currentPage;
		this.getBottomToolbar().inputItem.setValue(curPage);
	},

	onPagingKeyDown: function (field, e) {
		var me = this,
			k = e.getKey(),
			increment = e.shiftKey ? 10 : 1,
			pageNum, total = me.pdfDoc.numPages;

		if (k == e.RETURN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum !== false) {
				pageNum = Math.min(Math.max(1, pageNum), total);
				if (me.fireEvent('beforechange', me, pageNum) !== false) {
					me.renderPage(pageNum);
				}
			}
		} else if (k == e.HOME || k == e.END) {
			e.stopEvent();
			pageNum = k == e.HOME ? 1 : total;
			field.setValue(pageNum);
		} else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum) {
				if (k == e.DOWN || k == e.PAGE_DOWN) {
					increment *= -1;
				}
				pageNum += increment;
				if (pageNum >= 1 && pageNum <= total) {
					field.setValue(pageNum);
				}
			}
		}
	},

	onScaleChange: function (combo, record) {
		var me = this;

		me.pageScale = record.get(combo.valueField);
		me.renderPage(me.currentPage);
	},

	displayFullscreen: function () {
		var pdfBoxCfg = {
			easing        : 'elasticOut',
			resizeDuration: 0.6,
			close         : '&#215;',
			hideInfo      : 'auto',
			href          : this.src,
			title         : this.tile
		};
		Ext.ux.PdfBox.open(pdfBoxCfg);
	},

	setSrc: function (src) {
		this.src = src;
		return this.getDocument();
	}
	,

	getDocument: function () {
		var me = this;
		if (!!me.src) {
			PDFJS.getDocument(me.src).then(function (pdfDoc) {
				me.pdfDoc = pdfDoc;
				me.onLoad();
			});
		}
		return me;
	}
})
;

Ext.reg('filesplugin.pdfjspanel', Zarafa.plugins.files.ui.snippets.PDFjsPanel);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.WebODFPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg {Object} odfCanvas is Object of Odf.odfCanvas class.
	 * This object is responsible for rendering or opening
	 * the WebODF document in light box.
	 */
	odfCanvas: null,

	/**
	 * @cfg {Array} pages The pages in which all pages(slide) should be placed.
	 * This is only useful for ODP type of document.
	 */
	pages : [],

	/**
	 * @cfg {Number} currentPage The currentPage which contain index of
	 * the current page(slide) of the ODP document.
	 */
	currentPage : 0,

	/**
	 * @cfg {String} src The path to the odf file.
	 */
	src: null,

	/**
	 * @cfg {String} title The title of the odf document.
	 */
	title: '',

	/**
	 * @cfg{String} loadingMessage
	 * The text displayed when loading the PDF.
	 */
	loadingMessage: 'Loading PDF, please wait...',

	/**
	 * @cfg{String} beforePageText
	 * The text displayed before the input item.
	 */
	beforePageText: 'Page',

	/**
	 * @cfg{String} afterPageText
	 * Customizable piece of the default paging text. Note that this string is formatted using
	 *{0} as a token that is replaced by the number of total pages. This token should be preserved when overriding this
	 * string if showing the total page count is desired.
	 */
	afterPageText: 'of {0}',

	/**
	 * @cfg{String} firstText
	 * The quicktip text displayed for the first page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	firstText: 'First Page',

	/**
	 * @cfg{String} prevText
	 * The quicktip text displayed for the previous page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	prevText: 'Previous Page',

	/**
	 * @cfg{String} nextText
	 * The quicktip text displayed for the next page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	nextText: 'Next Page',

	/**
	 * @cfg{String} lastText
	 * The quicktip text displayed for the last page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	lastText: 'Last Page',

	/**
	 * @cfg{String} fullscreenText
	 * The quicktip text displayed for the fullscreen button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	fullscreenText: 'Fullscreen',

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the input field used to display and change the current page number.
	 */
	inputItemWidth: 30,

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the combobox used to change display scale of the PDF.
	 */
	scaleWidth: 60,

	/**
	 * @property {String} panelID The id of the webodf canvas panel
	 */
	panelID: null,

	/**
	 * @constructor
	 */
	constructor: function (config) {
		config = config || {};

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.webodfpanel',
			border: false,
			header: false, // hide title
			items : [{
				xtype    : 'component',
				cls: 'webodfpanel-outerDocumentContainer',
				autoEl   : {
					cn: [
						{tag: 'div', 'class': 'webodfpanel-canvas'}
					]
				},
				listeners: {
					afterrender: this.initODF.createDelegate(this)
				}
			}],
			bbar : this.getPagingToolbar()
		});

		Zarafa.plugins.files.ui.snippets.WebODFPanel.superclass.constructor.call(this, config);
	},

	getPagingToolbar: function () {
		var me = this;

		return {
			xtype: 'toolbar',
			height: 25,
			items: [{
				ref         : 'first',
				tooltip     : me.firstText,
				overflowText: me.firstText,
				iconCls     : 'x-tbar-page-first',
				disabled    : true,
				handler     : me.moveFirst,
				scope       : me
			}, {
				ref         : 'prev',
				tooltip     : me.prevText,
				overflowText: me.prevText,
				iconCls     : 'x-tbar-page-prev',
				disabled    : true,
				handler     : me.movePrevious,
				scope       : me
			}, '-', me.beforePageText, {
				xtype          : 'numberfield',
				ref            : 'inputItem',
				name           : 'inputItem',
				cls            : 'x-tbar-page-number',
				minValue       : 1,
				allowDecimals  : false,
				allowNegative  : false,
				enableKeyEvents: true,
				selectOnFocus  : true,
				submitValue    : false,
				width          : me.inputItemWidth,
				disabled       : true,
				margins        : '-1 2 3 2',
				listeners      : {
					scope  : me,
					keydown: me.onPagingKeyDown,
					blur   : me.onPagingBlur
				}
			}, {
				xtype  : 'tbtext',
				ref    : 'afterTextItem',
				text   : String.format(me.afterPageText, 1),
				margins: '0 5 0 0'
			}, '-', {
				ref         : 'next',
				tooltip     : me.nextText,
				overflowText: me.nextText,
				iconCls     : 'x-tbar-page-next',
				disabled    : true,
				handler     : me.moveNext,
				scope       : me
			}, {
				ref         : 'last',
				tooltip     : me.lastText,
				overflowText: me.lastText,
				iconCls     : 'x-tbar-page-last',
				disabled    : true,
				handler     : me.moveLast,
				scope       : me
			}, '->', {
				xtype       : 'combo',
				ref         : 'scaleCombo',
				triggerAction: 'all',
				lazyInit    : false,
				forceSelection: true,
				editable: false,
				autoSelect: true,
				disabled    : true,
				hidden: true, // dont show this element . (for now... TODO)
				width       : me.scaleWidth,
				store       : {
					xtype: 'jsonstore',
					autoDestroy : true,
					fields: ['scale', 'text'],
					data  : [
						{
							scale: 0.5,
							text: '50%'
						},{
							scale: 0.75,
							text: '75%'
						},{
							scale: 1,
							text: '100%'
						},{
							scale: 1.25,
							text: '125%'
						},{
							scale: 1.5,
							text: '150%'
						},{
							scale: 2,
							text: '200%'
						},{
							scale: 4,
							text: '400%'
						}
					]
				},
				valueField  : 'scale',
				displayField: 'text',
				mode        : 'local',
				listeners   : {
					scope : me,
					select : me.onScaleChange
				}
			}, {
				ref         : 'fullscreen',
				tooltip     : me.fullscreenText,
				overflowText: me.fullscreenText,
				iconCls     : 'files_icon_action_fullscreen',
				disabled    : false,
				handler     : me.displayFullscreen,
				scope       : me
			}]
		};
	},

	initODF: function () {
		// init gui elements
		this.canvasContainer = Ext.DomQuery.selectNode('div[class*=webodfpanel-canvas]', this.el.dom);

		this.odfCanvas = new odf.OdfCanvas(this.canvasContainer);
		this.odfCanvas.load(this.src);
	},

	moveFirst: function () {
		var me = this;
		if (me.fireEvent('beforechange', me, 1) !== false) {
			me.renderPage(1);
		}
	},

	movePrevious: function () {
		var me = this,
			prev = me.currentPage - 1;

		if (prev > 0) {
			if (me.fireEvent('beforechange', me, prev) !== false) {
				me.renderPage(prev);
			}
		}
	},

	moveNext: function () {
		var me = this,
			total = me.pdfDoc.numPages,
			next = me.currentPage + 1;

		if (next <= total) {
			if (me.fireEvent('beforechange', me, next) !== false) {
				me.renderPage(next);
			}
		}
	},

	moveLast: function () {
		var me = this,
			last = me.pdfDoc.numPages;

		if (me.fireEvent('beforechange', me, last) !== false) {
			me.renderPage(last);
		}
	},

	readPageFromInput: function () {
		var me = this, v = me.getBottomToolbar().inputItem.getValue(),
			pageNum = parseInt(v, 10);

		if (!v || isNaN(pageNum)) {
			me.getBottomToolbar().inputItem.setValue(me.currentPage);
			return false;
		}
		return pageNum;
	},

	onPagingFocus: function () {
		this.getBottomToolbar().inputItem.select();
	},

	onPagingBlur: function (e) {
		var curPage = this.getPageData().currentPage;
		this.getBottomToolbar().inputItem.setValue(curPage);
	},

	onPagingKeyDown: function (field, e) {
		var me = this,
			k = e.getKey(),
			increment = e.shiftKey ? 10 : 1,
			pageNum, total = me.pdfDoc.numPages;

		if (k == e.RETURN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum !== false) {
				pageNum = Math.min(Math.max(1, pageNum), total);
				if (me.fireEvent('beforechange', me, pageNum) !== false) {
					me.renderPage(pageNum);
				}
			}
		} else if (k == e.HOME || k == e.END) {
			e.stopEvent();
			pageNum = k == e.HOME ? 1 : total;
			field.setValue(pageNum);
		} else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum) {
				if (k == e.DOWN || k == e.PAGE_DOWN) {
					increment *= -1;
				}
				pageNum += increment;
				if (pageNum >= 1 && pageNum <= total) {
					field.setValue(pageNum);
				}
			}
		}
	},

	onScaleChange: function (combo, record) {
		var me = this;

		me.pageScale = record.get(combo.valueField);
		me.renderPage(me.currentPage);
	},

	displayFullscreen: function () {
		var webodfCfg = {
			resizeDuration : 0.40,
			overlayDuration : 0.6,
			href : this.src,
			title : this.title
		};
		Zarafa.plugins.webodf.WebOdfBox.open(webodfCfg);
	}
});

Ext.reg('filesplugin.webodfpanel', Zarafa.plugins.files.ui.snippets.WebODFPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.NavigatorTreePanel
 * @extends Zarafa.plugins.files.ui.Tree
 * @xtype filesplugin.navigatortreepanel
 *
 * The hierarchy tree panel implementation for files.
 */
Zarafa.plugins.files.ui.NavigatorTreePanel = Ext.extend(Zarafa.plugins.files.ui.Tree, {

	/**
	 * @property {String} nodeToSelect is the path of the node that should be selected.
	 */
	nodeToSelect : null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.tree',
			bodyCssClass : 'files-context-navigation-node',
			accountFilter: config.accountFilter
		});
		Zarafa.plugins.files.ui.NavigatorTreePanel.superclass.constructor.call(this, config);
	},

	/**
	 * Function called by Extjs when the {@link Zarafa.plugins.files.ui.Tree TreePanel}
	 * has been {@link #render rendered}. At this time all events can be registered.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.files.ui.NavigatorTreePanel.superclass.initEvents.apply(this, arguments);

		this.on({
			"click" : this.onNodeClick,
			"load" : this.treeNodeLoaded,
			"beforenodedrop" : this.onBeforeNodeDrop,
			"expandnode" : this.onExpandNode,
			"nodedragover" : this.onNodeDragOver,
			"afterrender" : this.onAfterRender,
			"contextmenu" : this.onContextMenu,
			scope : this
		});
	},

	/**
	 * The {@link Ext.tree.TreePanel#expandnode} event handler. It will silently load the children of the node.
	 * This is used to check if a node can be expanded or not.
	 *
	 * @param {Ext.tree.AsyncTreeNode} node
	 */
	onExpandNode: function (node)
	{
		node.attributes["cc"] = true;
		// check if a folder should be preloaded
		if(container.getSettingsModel().get('zarafa/v1/contexts/files/preload_folder')) {
			node.eachChild(function (child) {
				if (child.attributes["cc"] !== true) { // only check if it was not checked before
					child.attributes["cc"] = true;
					child.quietLoad();
				}
			});
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#beforenodedrop} event handler. It will move dropped nodes to the new
	 * location.
	 *
	 * @param event
	 * @returns {*}
	 */
	onBeforeNodeDrop: function (event)
	{
		if (Ext.isArray(event.data.selections)) {
			event.cancel = false;
			Ext.each(event.data.selections, function (record) {
				record.setDisabled(true);
			});

			return Zarafa.plugins.files.data.Actions.moveRecords(event.data.selections, event.target);
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#nodedragover} event handler. This function will check if dropping a node on
	 * this hovered node is allowed. (For example: same account check)
	 *
	 * @param event
	 * @returns {boolean}
	 */
	onNodeDragOver: function (event)
	{
		var ret = true;

		Ext.each(event.data.selections, function (record) {
			var srcId = record.get("id");
			var srcParentId = srcId.replace(/\\/g, '/').replace(/\/[^\/]*\/?$/, '') + "/";
			var trgId = event.target.id;

			// check if user wants to drop file to different account (not implemented yet)
			var srcAcc = Zarafa.plugins.files.data.Utils.File.getAccountId(srcId);
			var dstAcc = Zarafa.plugins.files.data.Utils.File.getAccountId(trgId);
			if (srcAcc != dstAcc) {
				ret = false;
				return false;
			}

			// check if we have a valid target
			if (srcId === trgId || trgId.slice(0, srcId.length) === srcId || srcParentId === trgId) {
				ret = false;
				return false;
			}
		}, this);

		return ret;
	},

	/**
	 * The {@link Ext.tree.TreePanel#click} event handler. This function will expand the node after it was clicked.
	 *
	 * @param node
	 */
	onNodeClick: function (node)
	{
		this.nodeToSelect = node.attributes.id;
		Zarafa.plugins.files.data.ComponentBox.getStore().loadPath(this.nodeToSelect);
		Zarafa.plugins.files.ui.FilesContextNavigatorBuilder.unselectAllNavPanels();

		var n = this.getNodeById(this.nodeToSelect);
		if (Ext.isDefined(n)) {
			n.expand();
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#afterrender} event handler. The DropZone needs to set up after the panel
	 * has been rendered.
	 *
	 * @param treepanel
	 */
	onAfterRender: function (treepanel)
	{
		this.dragZone.lock();
		this.dropZone = new Ext.tree.TreeDropZone(this, {
			ddGroup: this.ddGroup, appendOnly: this.ddAppendOnly === true
		});
	},

	/**
	 * The {@link Ext.tree.TreePanel#load} event handler. After a node was loaded - select it.
	 *
	 * @param node
	 */
	treeNodeLoaded: function (node)
	{
		this.getLoader().setReload(false);
		node.setLeaf(false); // we do always have folders!

		var nToSelect = null;
		if (!Ext.isEmpty(this.nodeToSelect)) {
			nToSelect = this.getNodeById(this.nodeToSelect);
		} else {
			nToSelect = this.getNodeById("#R#");
		}

		this.selectNode(nToSelect);
	},

	/**
	 * This method will reload the given node.
	 *
	 * @param nodeId
	 * @param child
	 */
	refreshNode: function (nodeId, child)
	{
		var node = this.getNodeById(nodeId);
		if (!Ext.isDefined(child)) {
			this.nodeToSelect = nodeId;
		}

		if (!Ext.isEmpty(node)) {
			this.getLoader().setReload(true);
			node.reload();

			if (node.hasChildNodes()) {
				node.expand();
			}
		}
	},

	/**
	 * Eventhandler for the context menu event. This will show the default content menu.
	 *
	 * @param node
	 * @param event
	 */
	onContextMenu: function (node, event)
	{
		Zarafa.core.data.UIFactory.openContextMenu(Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu'], [this.convertNodeToRecord(node)], {
			position: event.getXY(),
			context : Zarafa.plugins.files.data.ComponentBox.getContext()
		});
	}
});

Ext.reg('filesplugin.navigatortreepanel', Zarafa.plugins.files.ui.NavigatorTreePanel);
