text: /*!
   * elFinder - file manager for web
   * Version 2.1.13 (2016-07-11)
   * http://elfinder.org
   * 
   * Copyright 2009-2016, Studio 42
   * Licensed under a 3-clauses BSD license
   */
  (function($) {
  
  
  /*
   * File: /js/elFinder.js
   */
  
  /**
   * @class elFinder - file manager for web
   *
   * @author Dmitry (dio) Levashov
   **/
  window.elFinder = function(node, opts) {
  	//this.time('load');
  	
  	var self = this,
  		
  		/**
  		 * Node on which elfinder creating
  		 *
  		 * @type jQuery
  		 **/
  		node = $(node),
  		
  		/**
  		 * Store node contents.
  		 *
  		 * @see this.destroy
  		 * @type jQuery
  		 **/
  		prevContent = $('<div/>').append(node.contents()),
  		
  		/**
  		 * Store node inline styles
  		 *
  		 * @see this.destroy
  		 * @type String
  		 **/
  		prevStyle = node.attr('style'),
  		
  		/**
  		 * Instance ID. Required to get/set cookie
  		 *
  		 * @type String
  		 **/
  		id = node.attr('id') || '',
  		
  		/**
  		 * Events namespace
  		 *
  		 * @type String
  		 **/
  		namespace = 'elfinder-'+(id || Math.random().toString().substr(2, 7)),
  		
  		/**
  		 * Mousedown event
  		 *
  		 * @type String
  		 **/
  		mousedown = 'mousedown.'+namespace,
  		
  		/**
  		 * Keydown event
  		 *
  		 * @type String
  		 **/
  		keydown = 'keydown.'+namespace,
  		
  		/**
  		 * Keypress event
  		 *
  		 * @type String
  		 **/
  		keypress = 'keypress.'+namespace,
  		
  		/**
  		 * Is shortcuts/commands enabled
  		 *
  		 * @type Boolean
  		 **/
  		enabled = true,
  		
  		/**
  		 * Store enabled value before ajax requiest
  		 *
  		 * @type Boolean
  		 **/
  		prevEnabled = true,
  		
  		/**
  		 * List of build-in events which mapped into methods with same names
  		 *
  		 * @type Array
  		 **/
  		events = ['enable', 'disable', 'load', 'open', 'reload', 'select',  'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'dragstart', 'dragstop'],
  		
  		/**
  		 * Rules to validate data from backend
  		 *
  		 * @type Object
  		 **/
  		rules = {},
  		
  		/**
  		 * Current working directory hash
  		 *
  		 * @type String
  		 **/
  		cwd = '',
  		
  		/**
  		 * Current working directory options
  		 *
  		 * @type Object
  		 **/
  		cwdOptions = {
  			path          : '',
  			url           : '',
  			tmbUrl        : '',
  			disabled      : [],
  			separator     : '/',
  			archives      : [],
  			extract       : [],
  			copyOverwrite : true,
  			uploadOverwrite : true,
  			uploadMaxSize : 0,
  			jpgQuality    : 100,
  			tmb           : false // old API
  		},
  		
  		/**
  		 * Files/dirs cache
  		 *
  		 * @type Object
  		 **/
  		files = {},
  		
  		/**
  		 * Selected files hashes
  		 *
  		 * @type Array
  		 **/
  		selected = [],
  		
  		/**
  		 * Events listeners
  		 *
  		 * @type Object
  		 **/
  		listeners = {},
  		
  		/**
  		 * Shortcuts
  		 *
  		 * @type Object
  		 **/
  		shortcuts = {},
  		
  		/**
  		 * Buffer for copied files
  		 *
  		 * @type Array
  		 **/
  		clipboard = [],
  		
  		/**
  		 * Copied/cuted files hashes
  		 * Prevent from remove its from cache.
  		 * Required for dispaly correct files names in error messages
  		 *
  		 * @type Array
  		 **/
  		remember = [],
  		
  		/**
  		 * Queue for 'open' requests
  		 *
  		 * @type Array
  		 **/
  		queue = [],
  		
  		/**
  		 * Queue for only cwd requests e.g. `tmb`
  		 *
  		 * @type Array
  		 **/
  		cwdQueue = [],
  		
  		/**
  		 * Commands prototype
  		 *
  		 * @type Object
  		 **/
  		base = new self.command(self),
  		
  		/**
  		 * elFinder node width
  		 *
  		 * @type String
  		 * @default "auto"
  		 **/
  		width  = 'auto',
  		
  		/**
  		 * elFinder node height
  		 *
  		 * @type Number
  		 * @default 400
  		 **/
  		height = 400,
  		
  		/**
  		 * elfinder path for sound played on remove
  		 * @type String
  		 * @default ./sounds/
  		 **/
  		soundPath = './sounds/',
  				
  		beeper = $(document.createElement('audio')).hide().appendTo('body')[0],
  			
  		syncInterval,
  		
  		uiCmdMapPrev = '',
  		
  		open = function(data) {
  			var volumeid, contextmenu, emptyDirs = {}, stayDirs = {};
  			
  			if (self.api >= 2.1) {
  				self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
  				
  				// support volume driver option `uiCmdMap`
  				if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
  					uiCmdMapPrev = JSON.stringify(self.commandMap);
  					if (Object.keys(self.commandMap).length) {
  						// for contextmenu
  						contextmenu = self.getUI('contextmenu');
  						if (!contextmenu.data('cmdMaps')) {
  							contextmenu.data('cmdMaps', {});
  						}
  						volumeid = data.cwd? data.cwd.volumeid : null;
  						if (volumeid && !contextmenu.data('cmdMaps')[volumeid]) {
  							contextmenu.data('cmdMaps')[volumeid] = self.commandMap;
  						}
  					}
  				}
  			} else {
  				self.options.sync = 0;
  			}
  			
  			if (data.init) {
  				// init - reset cache
  				files = {};
  			} else {
  				// remove only files from prev cwd
  				// and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
  				$.each(Object.keys(files), function(n, i) {
  					var isDir = (files[i].mime === 'directory'),
  						phash = files[i].phash,
  						collapsed = self.res('class', 'navcollapse'),
  						pnav;
  					if (
  						(!isDir
  							|| emptyDirs[phash]
  							|| (!stayDirs[phash]
  								&& $('#'+self.navHash2Id(files[i].hash)).is(':hidden')
  								&& $('#'+self.navHash2Id(phash)).next('.elfinder-navbar-subtree').children().length > 100
  							)
  						)
  						&& (isDir || phash === cwd)
  						&& $.inArray(i, remember) === -1
  					) {
  						if (isDir && !emptyDirs[phash]) {
  							emptyDirs[phash] = true;
  						}
  						delete files[i];
  					} else if (isDir) {
  						stayDirs[phash] = true;
  					}
  				});
  				$.each(Object.keys(emptyDirs), function(n, i) {
  					var rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
  					$('#'+self.navHash2Id(i))
  					 .removeClass(rmClass)
  					 .next('.elfinder-navbar-subtree').empty();
  				});
  			}
  
  			self.sorters = [];
  			cwd = data.cwd.hash;
  			cache(data.files);
  			if (!files[cwd]) {
  				cache([data.cwd]);
  			}
  			self.lastDir(cwd);
  			
  			self.autoSync();
  		},
  		
  		/**
  		 * Store info about files/dirs in "files" object.
  		 *
  		 * @param  Array  files
  		 * @return void
  		 **/
  		cache = function(data) {
  			var defsorter = { name: true, perm: true, date: true,  size: true, kind: true },
  				sorterChk = (self.sorters.length === 0),
  				l         = data.length,
  				f, i;
  
  			for (i = 0; i < l; i++) {
  				f = data[i];
  				if (f.name && f.hash && f.mime) {
  					if (!f.phash) {
  						var name = 'volume_'+f.name,
  							i18 = self.i18n(name);
  
  						if (name != i18) {
  							f.i18 = i18;
  						}
  						
  						// set disabledCmds, tmbUrls for each volume
  						if (f.volumeid) {
  							f.disabled && (self.disabledCmds[f.volumeid] = f.disabled);
  							if (f.tmbUrl) {
  								self.tmbUrls[f.volumeid] = f.tmbUrl;
  							}
  							self.roots[f.volumeid] = f.hash;
  						}
  					}
  					if (sorterChk && f.phash === cwd) {
  						$.each(self.sortRules, function(key) {
  							if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
  								self.sorters.push(key);
  							}
  						});
  						sorterChk = false;
  					}
  					files[f.hash] = f;
  				} 
  			}
  		},
  		
  		/**
  		 * Exec shortcut
  		 *
  		 * @param  jQuery.Event  keydown/keypress event
  		 * @return void
  		 */
  		execShortcut = function(e) {
  			var code    = e.keyCode,
  				ctrlKey = !!(e.ctrlKey || e.metaKey);
  
  			if (enabled) {
  
  				$.each(shortcuts, function(i, shortcut) {
  					if (shortcut.type    == e.type 
  					&& shortcut.keyCode  == code 
  					&& shortcut.shiftKey == e.shiftKey 
  					&& shortcut.ctrlKey  == ctrlKey 
  					&& shortcut.altKey   == e.altKey) {
  						e.preventDefault()
  						e.stopPropagation();
  						shortcut.callback(e, self);
  						self.debug('shortcut-exec', i+' : '+shortcut.description);
  					}
  				});
  				
  				// prevent tab out of elfinder
  				if (code == 9 && !$(e.target).is(':input')) {
  					e.preventDefault();
  				}
  				
  				// cancel copy or cut by [Esc] key
  				if (code == 27 && self.clipboard().length) {
  					self.clipboard([]);
  				}
  
  			}
  		},
  		date = new Date(),
  		utc,
  		i18n
  		;
  
  
  	/**
  	 * Protocol version
  	 *
  	 * @type String
  	 **/
  	this.api = null;
  	
  	/**
  	 * elFinder use new api
  	 *
  	 * @type Boolean
  	 **/
  	this.newAPI = false;
  	
  	/**
  	 * elFinder use old api
  	 *
  	 * @type Boolean
  	 **/
  	this.oldAPI = false;
  	
  	/**
  	 * Net drivers names
  	 *
  	 * @type Array
  	 **/
  	this.netDrivers = [];
  	/**
  	 * User os. Required to bind native shortcuts for open/rename
  	 *
  	 * @type String
  	 **/
  	this.OS = navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1  ? 'win' : 'other';
  	
  	/**
  	 * User browser UA.
  	 * jQuery.browser: version deprecated: 1.3, removed: 1.9
  	 *
  	 * @type Object
  	 **/
  	this.UA = (function(){
  		var webkit = !document.uniqueID && !window.opera && !window.sidebar && window.localStorage && typeof window.orientation == "undefined";
  		return {
  			// Browser IE <= IE 6
  			ltIE6:typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
  			// Browser IE <= IE 7
  			ltIE7:typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
  			// Browser IE <= IE 8
  			ltIE8:typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
  			IE:document.uniqueID,
  			Firefox:window.sidebar,
  			Opera:window.opera,
  			Webkit:webkit,
  			Chrome:webkit && window.chrome,
  			Safari:webkit && !window.chrome,
  			Mobile:typeof window.orientation != "undefined",
  			Touch:typeof window.ontouchstart != "undefined",
  			iOS: navigator.platform.match(/^iP(?:[ao]d|hone)/)
  		};
  	})();
  	
  	/**
  	 * Configuration options
  	 *
  	 * @type Object
  	 **/
  	this.options = $.extend(true, {}, this._options, opts||{});
  	
  	if (opts.ui) {
  		this.options.ui = opts.ui;
  	}
  	
  	if (opts.commands) {
  		this.options.commands = opts.commands;
  	}
  	
  	if (opts.uiOptions && opts.uiOptions.toolbar) {
  		this.options.uiOptions.toolbar = opts.uiOptions.toolbar;
  	}
  
  	if (opts.uiOptions && opts.uiOptions.cwd && opts.uiOptions.cwd.listView && opts.uiOptions.cwd.listView.columns) {
  		this.options.uiOptions.cwd.listView.columns = opts.uiOptions.cwd.listView.columns;
  	}
  	if (opts.uiOptions && opts.uiOptions.cwd && opts.uiOptions.cwd.listView && opts.uiOptions.cwd.listView.columnsCustomName) {
  		this.options.uiOptions.cwd.listView.columnsCustomName = opts.uiOptions.cwd.listView.columnsCustomName;
  	}
  
  	if (! this.options.enableAlways && $('body').children().length === 2) { // only node and beeper
  		this.options.enableAlways = true;
  	}
  
  	// configure for CORS
  	(function(){
  		var parseUrl = document.createElement('a'),
  			parseUploadUrl;
  		parseUrl.href = opts.url;
  		if (opts.urlUpload && (opts.urlUpload !== opts.url)) {
  			parseUploadUrl = document.createElement('a');
  			parseUploadUrl.href = opts.urlUpload;
  		}
  		if (window.location.host !== parseUrl.host || (parseUploadUrl && (window.location.host !== parseUploadUrl.host))) {
  			if (!$.isPlainObject(self.options.customHeaders)) {
  				self.options.customHeaders = {};
  			}
  			if (!$.isPlainObject(self.options.xhrFields)) {
  				self.options.xhrFields = {};
  			}
  			self.options.requestType = 'post';
  			self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest';
  			self.options.xhrFields['withCredentials'] = true;
  		}
  	})();
  
  	$.extend(this.options.contextmenu, opts.contextmenu);
  	
  	/**
  	 * Ajax request type
  	 *
  	 * @type String
  	 * @default "get"
  	 **/
  	this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get';
  	
  	/**
  	 * Any data to send across every ajax request
  	 *
  	 * @type Object
  	 * @default {}
  	 **/
  	this.customData = $.isPlainObject(this.options.customData) ? this.options.customData : {};
  
  	/**
  	 * Any custom headers to send across every ajax request
  	 *
  	 * @type Object
  	 * @default {}
  	*/
  	this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {};
  
  	/**
  	 * Any custom xhrFields to send across every ajax request
  	 *
  	 * @type Object
  	 * @default {}
  	 */
  	this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {};
  
  	/**
  	 * command names for into queue for only cwd requests
  	 * these commands aborts before `open` request
  	 *
  	 * @type Array
  	 * @default ['tmb']
  	 */
  	this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb'];
  
  	/**
  	 * ID. Required to create unique cookie name
  	 *
  	 * @type String
  	 **/
  	this.id = id;
  	
  	/**
  	 * ui.nav id prefix
  	 * 
  	 * @type String
  	 */
  	this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-';
  	
  	/**
  	 * ui.cwd id prefix
  	 * 
  	 * @type String
  	 */
  	this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : '';
  	
  	// Increment elFinder.prototype.uniqueid
  	++elFinder.prototype.uniqueid;
  	
  	/**
  	 * URL to upload files
  	 *
  	 * @type String
  	 **/
  	this.uploadURL = opts.urlUpload || opts.url;
  	
  	/**
  	 * Events namespace
  	 *
  	 * @type String
  	 **/
  	this.namespace = namespace;
  
  	/**
  	 * Interface language
  	 *
  	 * @type String
  	 * @default "en"
  	 **/
  	this.lang = this.i18[this.options.lang] && this.i18[this.options.lang].messages ? this.options.lang : 'en';
  	
  	i18n = this.lang == 'en' 
  		? this.i18['en'] 
  		: $.extend(true, {}, this.i18['en'], this.i18[this.lang]);
  	
  	/**
  	 * Interface direction
  	 *
  	 * @type String
  	 * @default "ltr"
  	 **/
  	this.direction = i18n.direction;
  	
  	/**
  	 * i18 messages
  	 *
  	 * @type Object
  	 **/
  	this.messages = i18n.messages;
  	
  	/**
  	 * Date/time format
  	 *
  	 * @type String
  	 * @default "m.d.Y"
  	 **/
  	this.dateFormat = this.options.dateFormat || i18n.dateFormat;
  	
  	/**
  	 * Date format like "Yesterday 10:20:12"
  	 *
  	 * @type String
  	 * @default "{day} {time}"
  	 **/
  	this.fancyFormat = this.options.fancyDateFormat || i18n.fancyDateFormat;
  
  	/**
  	 * Today timestamp
  	 *
  	 * @type Number
  	 **/
  	this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000;
  	
  	/**
  	 * Yesterday timestamp
  	 *
  	 * @type Number
  	 **/
  	this.yesterday = this.today - 86400;
  	
  	utc = this.options.UTCDate ? 'UTC' : '';
  	
  	this.getHours    = 'get'+utc+'Hours';
  	this.getMinutes  = 'get'+utc+'Minutes';
  	this.getSeconds  = 'get'+utc+'Seconds';
  	this.getDate     = 'get'+utc+'Date';
  	this.getDay      = 'get'+utc+'Day';
  	this.getMonth    = 'get'+utc+'Month';
  	this.getFullYear = 'get'+utc+'FullYear';
  	
  	/**
  	 * Css classes 
  	 *
  	 * @type String
  	 **/
  	this.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'
  			+(this.direction == 'rtl' ? 'rtl' : 'ltr')
  			+(this.UA.Touch? (' elfinder-touch' + (this.options.resizable ? ' touch-punch' : '')) : '')
  			+(this.UA.Mobile? ' elfinder-mobile' : '')
  			+' '+this.options.cssClass;
  
  	/**
  	 * elFinder node z-index (auto detect on elFinder load)
  	 *
  	 * @type null | Number
  	 **/
  	this.zIndex;
  
  	/**
  	 * Current search status
  	 * 
  	 * @type Object
  	 */
  	this.searchStatus = {
  		state  : 0, // 0: search ended, 1: search started, 2: in search result
  		query  : '',
  		target : '',
  		mime   : '',
  		ininc  : false // in incremental search
  	};
  
  	/**
  	 * Method to store/fetch data
  	 *
  	 * @type Function
  	 **/
  	this.storage = (function() {
  		try {
  			return 'localStorage' in window && window['localStorage'] !== null ? self.localStorage : self.cookie;
  		} catch (e) {
  			return self.cookie;
  		}
  	})();
  
  	this.viewType = this.storage('view') || this.options.defaultView || 'icons';
  
  	this.sortType = this.storage('sortType') || this.options.sortType || 'name';
  	
  	this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc';
  
  	this.sortStickFolders = this.storage('sortStickFolders');
  
  	if (this.sortStickFolders === null) {
  		this.sortStickFolders = !!this.options.sortStickFolders;
  	} else {
  		this.sortStickFolders = !!this.sortStickFolders
  	}
  
  	this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules);
  	
  	$.each(this.sortRules, function(name, method) {
  		if (typeof method != 'function') {
  			delete self.sortRules[name];
  		} 
  	});
  	
  	this.compare = $.proxy(this.compare, this);
  	
  	/**
  	 * Delay in ms before open notification dialog
  	 *
  	 * @type Number
  	 * @default 500
  	 **/
  	this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500;
  	
  	/**
  	 * Dragging UI Helper object
  	 *
  	 * @type jQuery | null
  	 **/
  	this.draggingUiHelper = null;
  	
  	/**
  	 * Base draggable options
  	 *
  	 * @type Object
  	 **/
  	this.draggable = {
  		appendTo   : 'body',
  		addClasses : true,
  		distance   : 4,
  		revert     : true,
  		refreshPositions : false,
  		cursor     : 'crosshair',
  		cursorAt   : {left : 50, top : 47},
  		scroll     : false,
  		start      : function(e, ui) {
  			var helper   = ui.helper,
  				targets  = $.map(helper.data('files')||[], function(h) { return h || null ;}),
  				locked   = false,
  				navbar   = self.getUI('navbar'),
  				cwd      = self.getUI('cwd').parent(),
  				autoScr  = {
  					navbarUp   : function() {
  						navbar.scrollTop(Math.max(0, navbar.scrollTop() - helper.data('autoScrVal')));
  					},
  					navbarDown : function() {
  						navbar.scrollTop(navbar.scrollTop() + helper.data('autoScrVal'));
  					},
  					cwdUp     : function() {
  						cwd.scrollTop(Math.max(0, cwd.scrollTop() - helper.data('autoScrVal')));
  					},
  					cwdDown   : function() {
  						cwd.scrollTop(cwd.scrollTop() + helper.data('autoScrVal'));
  					}
  				},
  				cnt, h, wz, cwdOffset, wzOffset;
  			
  			self.draggingUiHelper = helper;
  			cnt = targets.length;
  			while (cnt--) {
  				h = targets[cnt];
  				if (files[h].locked) {
  					locked = true;
  					helper.data('locked', true);
  					break;
  				}
  			}
  			!locked && self.trigger('lockfiles', {files : targets});
  
  			cwdOffset = cwd.offset();
  			wz = self.getUI('workzone');
  			wzOffset = wz.offset();
  			helper.data('wzpos', {
  				ltr : self.direction === 'ltr',
  				cwdEdge : (self.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width(),
  				wzTop : wzOffset.top,
  				wzBottom : wzOffset.top + wz.height()
  			});
  			helper.data('autoScrTm', setInterval(function() {
  				if (helper.data('autoScr')) {
  					autoScr[helper.data('autoScr')]();
  				}
  			}, 50));
  		},
  		drag       : function(e, ui) {
  			var helper = ui.helper,
  				wzpos  = helper.data('wzpos'),
  				autoUp;
  			
  			if ((autoUp = wzpos.wzTop > e.pageY) || wzpos.wzBottom < e.pageY) {
  				if (wzpos.cwdEdge > e.pageX) {
  					helper.data('autoScr', (wzpos.ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down'));
  				} else {
  					helper.data('autoScr', (wzpos.ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down'));
  				}
  				helper.data('autoScrVal', Math.pow((autoUp? wzpos.wzTop - e.pageY : e.pageY - wzpos.wzBottom), 1.3));
  			} else {
  				if (helper.data('autoScr')) {
  					helper.data('refreshPositions', 1).data('autoScr', null);
  				}
  			}
  			if (helper.data('refreshPositions') && $(this).elfUiWidgetInstance('draggable')) {
  				if (helper.data('refreshPositions') > 0) {
  					$(this).draggable('option', { refreshPositions : true, elfRefresh : true });
  					helper.data('refreshPositions', -1);
  				} else {
  					$(this).draggable('option', { refreshPositions : false, elfRefresh : false });
  					helper.data('refreshPositions', null);
  				}
  			}
  		},
  		stop       : function(e, ui) {
  			var helper = ui.helper,
  				files;
  			
  			$(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false });
  			self.draggingUiHelper = null;
  			self.trigger('focus').trigger('dragstop');
  			if (! helper.data('droped')) {
  				files = $.map(helper.data('files')||[], function(h) { return h || null ;});
  				self.trigger('unlockfiles', {files : files});
  				self.trigger('selectfiles', {files : files});
  			}
  			self.enable();
  			
  			helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm'));
  		},
  		helper     : function(e, ui) {
  			var element = this.id ? $(this) : $(this).parents('[id]:first'),
  				helper  = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"/></div>'),
  				icon    = function(f) {
  					var mime = f.mime, i, tmb = self.tmb(f);
  					i = '<div class="elfinder-cwd-icon '+self.mime2class(mime)+' ui-corner-all"/>';
  					if (tmb) {
  						i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  					}
  					return i;
  				},
  				hashes, l, ctr;
  			
  			self.draggingUiHelper && self.draggingUiHelper.stop(true, true);
  			
  			self.trigger('dragstart', {target : element[0], originalEvent : e});
  			
  			hashes = element.hasClass(self.res('class', 'cwdfile')) 
  				? self.selected() 
  				: [self.navId2Hash(element.attr('id'))];
  			
  			helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0);
  
  			if ((l = hashes.length) > 1) {
  				helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>');
  			}
  			
  			$(document).on(keydown + ' keyup.' + namespace, function(e){
  				var chk = (e.shiftKey||e.ctrlKey||e.metaKey);
  				if (ctr !== chk) {
  					ctr = chk;
  					if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) {
  						helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr);
  						self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper});
  					}
  				}
  			});
  			
  			return helper;
  		}
  	};
  	
  	/**
  	 * Base droppable options
  	 *
  	 * @type Object
  	 **/
  	this.droppable = {
  		greedy     : true,
  		tolerance  : 'pointer',
  		accept     : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename',
  		hoverClass : this.res('class', 'adroppable'),
  		classes    : { // Deprecated hoverClass jQueryUI>=1.12.0
  			'ui-droppable-hover': this.res('class', 'adroppable')
  		},
  		autoDisable: true, // elFinder original, see jquery.elfinder.js
  		drop : function(e, ui) {
  			var dst     = $(this),
  				targets = $.map(ui.helper.data('files')||[], function(h) { return h || null }),
  				result  = [],
  				dups    = [],
  				faults  = [],
  				isCopy  = ui.helper.hasClass('elfinder-drag-helper-plus'),
  				c       = 'class',
  				cnt, hash, i, h;
  			
  			if (ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) {
  				return false;
  			}
  			if (dst.hasClass(self.res(c, 'cwdfile'))) {
  				hash = self.cwdId2Hash(dst.attr('id'));
  			} else if (dst.hasClass(self.res(c, 'navdir'))) {
  				hash = self.navId2Hash(dst.attr('id'));
  			} else {
  				hash = cwd;
  			}
  
  			cnt = targets.length;
  			
  			while (cnt--) {
  				h = targets[cnt];
  				// ignore drop into itself or in own location
  				if (h != hash && files[h].phash != hash) {
  					result.push(h);
  				} else {
  					((isCopy && h !== hash && files[hash].write)? dups : faults).push(h);
  				}
  			}
  			
  			if (faults.length) {
  				return false;
  			}
  			
  			ui.helper.data('droped', true);
  			
  			if (dups.length) {
  				ui.helper.hide();
  				self.exec('duplicate', dups);
  			}
  			
  			if (result.length) {
  				ui.helper.hide();
  				self.clipboard(result, !isCopy);
  				self.exec('paste', hash, void 0, hash).always(function(){
  					self.trigger('unlockfiles', {files : targets});
  				});
  				self.trigger('drop', {files : targets});
  			}
  		}
  	};
  	
  	/**
  	 * Return true if filemanager is active
  	 *
  	 * @return Boolean
  	 **/
  	this.enabled = function() {
  		return node.is(':visible') && enabled;
  	};
  	
  	/**
  	 * Return true if filemanager is visible
  	 *
  	 * @return Boolean
  	 **/
  	this.visible = function() {
  		return node.is(':visible');
  	};
  	
  	/**
  	 * Return root dir hash for current working directory
  	 * 
  	 * @return String
  	 */
  	this.root = function(hash) {
  		hash = hash || cwd;
  		var dir = files[hash], i;
  		
  		while (dir && dir.phash) {
  			dir = files[dir.phash]
  		}
  		if (dir) {
  			return dir.hash;
  		}
  		
  		$.each(self.roots, function(id, rhash) {
  			if (hash.indexOf(id) === 0) {
  				dir = rhash;
  				return false;
  			}
  		});
  		if (dir) {
  			return dir;
  		}
  		
  		while (i in files && files.hasOwnProperty(i)) {
  			dir = files[i]
  			if (!dir.phash && !dir.mime == 'directory' && dir.read) {
  				return dir.hash;
  			}
  		}
  		
  		return '';
  	};
  	
  	/**
  	 * Return current working directory info
  	 * 
  	 * @return Object
  	 */
  	this.cwd = function() {
  		return files[cwd] || {};
  	};
  	
  	/**
  	 * Return required cwd option
  	 * 
  	 * @param  String  option name
  	 * @return mixed
  	 */
  	this.option = function(name) {
  		return cwdOptions[name]||'';
  	};
  	
  	/**
  	 * Return file data from current dir or tree by it's hash
  	 * 
  	 * @param  String  file hash
  	 * @return Object
  	 */
  	this.file = function(hash) { 
  		return files[hash]; 
  	};
  	
  	/**
  	 * Return all cached files
  	 * 
  	 * @return Array
  	 */
  	this.files = function() {
  		return $.extend(true, {}, files);
  	};
  	
  	/**
  	 * Return list of file parents hashes include file hash
  	 * 
  	 * @param  String  file hash
  	 * @return Array
  	 */
  	this.parents = function(hash) {
  		var parents = [],
  			dir;
  		
  		while ((dir = this.file(hash))) {
  			parents.unshift(dir.hash);
  			hash = dir.phash;
  		}
  		return parents;
  	};
  	
  	this.path2array = function(hash, i18) {
  		var file, 
  			path = [];
  			
  		while (hash && (file = files[hash]) && file.hash) {
  			path.unshift(i18 && file.i18 ? file.i18 : file.name);
  			hash = file.phash;
  		}
  			
  		return path;
  	};
  	
  	/**
  	 * Return file path
  	 * 
  	 * @param  Object  file
  	 * @return String
  	 */
  	this.path = function(hash, i18) { 
  		return files[hash] && files[hash].path
  			? files[hash].path
  			: this.path2array(hash, i18).join(cwdOptions.separator);
  	};
  	
  	/**
  	 * Return file url if set
  	 * 
  	 * @param  String  file hash
  	 * @return String
  	 */
  	this.url = function(hash) {
  		var file = files[hash];
  		
  		if (!file || !file.read) {
  			return '';
  		}
  		
  		if (file.url == '1') {
  			this.request({
  				data : {cmd : 'url', target : hash},
  				preventFail : true,
  				options: {async: false}
  			})
  			.done(function(data) {
  				file.url = data.url || '';
  			})
  			.fail(function() {
  				file.url = '';
  			});
  		}
  		
  		if (file.url) {
  			return file.url;
  		}
  		
  		if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  			return cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/')
  		}
  
  		var params = $.extend({}, this.customData, {
  			cmd: 'file',
  			target: file.hash
  		});
  		if (this.oldAPI) {
  			params.cmd = 'open';
  			params.current = file.phash;
  		}
  		return this.options.url + (this.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true);
  	};
  	
  	/**
  	 * Convert from relative URL to abstract URL based on current URL
  	 * 
  	 * @param  String  URL
  	 * @return String
  	 */
  	this.convAbsUrl = function(url) {
  		if (url.match(/^http/i)) {
  			return url;
  		}
  		var root = window.location.protocol + '//' + window.location.host,
  			reg  = /[^\/]+\/\.\.\//,
  			ret;
  		if (url.substr(0, 1) === '/') {
  			ret = root + url;
  		} else {
  			ret = root + window.location.pathname + url;
  		}
  		ret = ret.replace('/./', '/');
  		while(reg.test(ret)) {
  			ret = ret.replace(reg, '');
  		}
  		return ret;
  	};
  	
  	/**
  	 * Return file url for open in elFinder
  	 * 
  	 * @param  String  file hash
  	 * @param  Boolean for download link
  	 * @return String
  	 */
  	this.openUrl = function(hash, download) {
  		var file = files[hash],
  			url  = '';
  		
  		if (!file || !file.read) {
  			return '';
  		}
  		
  		if (!download) {
  			if (file.url) {
  				if (file.url != 1) {
  					return file.url;
  				}
  			} else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  				return cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  			}
  		}
  		
  		url = this.options.url;
  		url = url + (url.indexOf('?') === -1 ? '?' : '&')
  			+ (this.oldAPI ? 'cmd=open&current='+file.phash : 'cmd=file')
  			+ '&target=' + file.hash;
  		
  		if (download) {
  			url += '&download=1';
  		}
  		
  		$.each(this.options.customData, function(key, val) {
  			url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  		});
  		
  		return url;
  	};
  	
  	/**
  	 * Return thumbnail url
  	 * 
  	 * @param  Object  file object
  	 * @return String
  	 */
  	this.tmb = function(file) {
  		var geturl = function(hash){
  				var turl = '';
  				$.each(self.tmbUrls, function(i, u){
  					if (hash.indexOf(i) === 0) {
  						turl = self.tmbUrls[i];
  						return false;
  					}
  				});
  				return turl;
  			},
  			tmbUrl = (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0)? geturl(file.hash) : cwdOptions['tmbUrl'],
  			cls    = 'elfinder-cwd-bgurl',
  			url    = '';
  
  		if ($.isPlainObject(file)) {
  			if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) {
  				url = self.openUrl(file.hash);
  				cls += ' elfinder-cwd-bgself';
  			} else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) {
  				url = tmbUrl + file.tmb;
  			}
  			if (url) {
  				return { url: url, className: cls };
  			}
  		}
  		
  		return false;
  	};
  	
  	/**
  	 * Return selected files hashes
  	 *
  	 * @return Array
  	 **/
  	this.selected = function() {
  		return selected.slice(0);
  	};
  	
  	/**
  	 * Return selected files info
  	 * 
  	 * @return Array
  	 */
  	this.selectedFiles = function() {
  		return $.map(selected, function(hash) { return files[hash] ? $.extend({}, files[hash]) : null });
  	};
  	
  	/**
  	 * Return true if file with required name existsin required folder
  	 * 
  	 * @param  String  file name
  	 * @param  String  parent folder hash
  	 * @return Boolean
  	 */
  	this.fileByName = function(name, phash) {
  		var hash;
  	
  		for (hash in files) {
  			if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) {
  				return files[hash];
  			}
  		}
  	};
  	
  	/**
  	 * Valid data for required command based on rules
  	 * 
  	 * @param  String  command name
  	 * @param  Object  cammand's data
  	 * @return Boolean
  	 */
  	this.validResponse = function(cmd, data) {
  		return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data);
  	};
  	
  	/**
  	 * Return bytes from ini formated size
  	 * 
  	 * @param  String  ini formated size
  	 * @return Integer
  	 */
  	this.returnBytes = function(val) {
  		var last;
  		if (isNaN(val)) {
  			if (! val) {
  				val = '';
  			}
  			// for ex. 1mb, 1KB
  			val = val.replace(/b$/i, '');
  			last = val.charAt(val.length - 1).toLowerCase();
  			val = val.replace(/[tgmk]$/i, '');
  			if (last == 't') {
  				val = val * 1024 * 1024 * 1024 * 1024;
  			} else if (last == 'g') {
  				val = val * 1024 * 1024 * 1024;
  			} else if (last == 'm') {
  				val = val * 1024 * 1024;
  			} else if (last == 'k') {
  				val = val * 1024;
  			}
  			val = isNaN(val)? 0 : parseInt(val);
  		} else {
  			val = parseInt(val);
  			if (val < 1) val = 0;
  		}
  		return val;
  	};
  	
  	/**
  	 * Proccess ajax request.
  	 * Fired events :
  	 * @todo
  	 * @example
  	 * @todo
  	 * @return $.Deferred
  	 */
  	this.request = function(opts) { 
  		var self     = this,
  			o        = this.options,
  			dfrd     = $.Deferred(),
  			// request data
  			data     = $.extend({}, o.customData, {mimes : o.onlyMimes}, opts.data || opts),
  			// command name
  			cmd      = data.cmd,
  			isOpen   = (cmd === 'open'),
  			// call default fail callback (display error dialog) ?
  			deffail  = !(opts.preventDefault || opts.preventFail),
  			// call default success callback ?
  			defdone  = !(opts.preventDefault || opts.preventDone),
  			// options for notify dialog
  			notify   = $.extend({}, opts.notify),
  			// make cancel button
  			cancel   = !!opts.cancel,
  			// do not normalize data - return as is
  			raw      = !!opts.raw,
  			// sync files on request fail
  			syncOnFail = opts.syncOnFail,
  			// use lazy()
  			lazy     = !!opts.lazy,
  			// open notify dialog timeout
  			timeout,
  			// request options
  			options = $.extend({
  				url      : o.url,
  				async    : true,
  				type     : this.requestType,
  				dataType : 'json',
  				cache    : false,
  				// timeout  : 100,
  				data     : data,
  				headers  : this.customHeaders,
  				xhrFields: this.xhrFields
  			}, opts.options || {}),
  			/**
  			 * Default success handler. 
  			 * Call default data handlers and fire event with command name.
  			 *
  			 * @param Object  normalized response data
  			 * @return void
  			 **/
  			done = function(data) {
  				data.warning && self.error(data.warning);
  				
  				isOpen && open($.extend(true, {}, data));
  
  				// fire some event to update cache/ui
  				data.removed && data.removed.length && self.remove(data);
  				data.added   && data.added.length   && self.add(data);
  				data.changed && data.changed.length && self.change(data);
  				
  				// fire event with command name
  				self.trigger(cmd, data);
  				
  				// force update content
  				data.sync && self.sync();
  			},
  			/**
  			 * Request error handler. Reject dfrd with correct error message.
  			 *
  			 * @param jqxhr  request object
  			 * @param String request status
  			 * @return void
  			 **/
  			error = function(xhr, status) {
  				var error;
  				
  				switch (status) {
  					case 'abort':
  						error = xhr.quiet ? '' : ['errConnect', 'errAbort'];
  						break;
  					case 'timeout':	    
  						error = ['errConnect', 'errTimeout'];
  						break;
  					case 'parsererror': 
  						error = ['errResponse', 'errDataNotJSON'];
  						break;
  					default:
  						if (xhr.status == 403) {
  							error = ['errConnect', 'errAccess'];
  						} else if (xhr.status == 404) {
  							error = ['errConnect', 'errNotFound'];
  						} else {
  							if (xhr.status == 414 && options.type === 'get') {
  								// retry by POST method
  								options.type = 'post';
  								dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success);
  								return;
  							}
  							error = ['errConnect', 'HTTP error ' + xhr.status];
  						} 
  				}
  				
  				dfrd.reject(error, xhr, status);
  			},
  			/**
  			 * Request success handler. Valid response data and reject/resolve dfrd.
  			 *
  			 * @param Object  response data
  			 * @param String request status
  			 * @return void
  			 **/
  			success = function(response) {
  				// Set currrent request command name
  				self.currentReqCmd = cmd;
  				
  				if (raw) {
  					return dfrd.resolve(response);
  				}
  				
  				if (!response) {
  					return dfrd.reject(['errResponse', 'errDataEmpty'], xhr);
  				} else if (!$.isPlainObject(response)) {
  					return dfrd.reject(['errResponse', 'errDataNotJSON'], xhr);
  				} else if (response.error) {
  					return dfrd.reject(response.error, xhr);
  				} else if (!self.validResponse(cmd, response)) {
  					return dfrd.reject('errResponse', xhr);
  				}
  				
  				var resolve = function() {
  					response = self.normalize(response);
  
  					if (!self.api) {
  						self.api    = response.api || 1;
  						if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') {
  							self.api = '2.1';
  						}
  						self.newAPI = self.api >= 2;
  						self.oldAPI = !self.newAPI;
  					}
  					
  					if (response.options) {
  						cwdOptions = $.extend({}, cwdOptions, response.options);
  					}
  
  					if (response.netDrivers) {
  						self.netDrivers = response.netDrivers;
  					}
  
  					if (isOpen && !!data.init) {
  						self.uplMaxSize = self.returnBytes(response.uplMaxSize);
  						self.uplMaxFile = !!response.uplMaxFile? parseInt(response.uplMaxFile) : 20;
  					}
  
  					dfrd.resolve(response);
  					response.debug && self.debug('backend-debug', response.debug);
  				};
  				
  				lazy? self.lazy(resolve) : resolve();
  			},
  			xhr, _xhr,
  			abort = function(e){
  				if (e.type == 'autosync') {
  					if (e.data.action != 'stop') return;
  				} else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') {
  					if (!e.data.added || !e.data.added.length) {
  						return;
  					}
  				}
  				if (xhr.state() == 'pending') {
  					xhr.quiet = true;
  					xhr.abort();
  					if (e.type != 'unload' && e.type != 'destroy') {
  						self.autoSync();
  					}
  				}
  			};
  
  		defdone && dfrd.done(done);
  		dfrd.fail(function(error) {
  			if (error) {
  				deffail ? self.error(error) : self.debug('error', self.i18n(error));
  			}
  		})
  		
  		if (!cmd) {
  			return dfrd.reject('errCmdReq');
  		}
  
  		if (syncOnFail) {
  			dfrd.fail(function(error) {
  				error && self.sync();
  			});
  		}
  
  		if (notify.type && notify.cnt) {
  			if (cancel) {
  				notify.cancel = dfrd;
  			}
  			timeout = setTimeout(function() {
  				self.notify(notify);
  				dfrd.always(function() {
  					notify.cnt = -(parseInt(notify.cnt)||0);
  					self.notify(notify);
  				})
  			}, self.notifyDelay)
  			
  			dfrd.always(function() {
  				clearTimeout(timeout);
  			});
  		}
  		
  		// quiet abort not completed "open" requests
  		if (isOpen) {
  			while ((_xhr = queue.pop())) {
  				if (_xhr.state() == 'pending') {
  					_xhr.quiet = true;
  					_xhr.abort();
  				}
  			}
  			if (cwd !== data.target) {
  				while ((_xhr = cwdQueue.pop())) {
  					if (_xhr.state() == 'pending') {
  						_xhr.quiet = true;
  						_xhr.abort();
  					}
  				}
  			}
  		}
  
  		// trigger abort autoSync for commands to add the item
  		if ($.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) {
  			self.trigger('openxhrabort');
  		}
  
  		delete options.preventFail
  
  		dfrd.xhr = xhr = this.transport.send(options).fail(error).done(success);
  		
  		if (isOpen || (data.compare && cmd === 'info')) {
  			// add autoSync xhr into queue
  			queue.unshift(xhr);
  			// bind abort()
  			! isOpen && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  			dfrd.always(function() {
  				var ndx = $.inArray(xhr, queue);
  				! isOpen && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  				ndx !== -1 && queue.splice(ndx, 1);
  			});
  		} else if ($.inArray(cmd, this.abortCmdsOnOpen) !== -1) {
  			// add "open" xhr, only cwd xhr into queue
  			cwdQueue.unshift(xhr);
  			dfrd.always(function() {
  				var ndx = $.inArray(xhr, cwdQueue);
  				ndx !== -1 && cwdQueue.splice(ndx, 1);
  			});
  		}
  		
  		// abort pending xhr on window unload or elFinder destroy
  		self.bind('unload destroy', abort);
  		dfrd.always(function() {
  			self.unbind('unload destroy', abort);
  		});
  		
  		return dfrd;
  	};
  	
  	/**
  	 * Compare current files cache with new files and return diff
  	 * 
  	 * @param  Array  new files
  	 * @return Object
  	 */
  	this.diff = function(incoming, onlydir) {
  		var raw       = {},
  			added     = [],
  			removed   = [],
  			changed   = [],
  			isChanged = function(hash) {
  				var l = changed.length;
  
  				while (l--) {
  					if (changed[l].hash == hash) {
  						return true;
  					}
  				}
  			};
  			
  		$.each(incoming, function(i, f) {
  			raw[f.hash] = f;
  		});
  			
  		// find removed
  		$.each(files, function(hash, f) {
  			if (!onlydir || f.phash === onlydir) {
  				!raw[hash] && removed.push(hash);
  			}
  		});
  		
  		// compare files
  		$.each(raw, function(hash, file) {
  			var origin = files[hash];
  
  			if (!origin) {
  				added.push(file);
  			} else {
  				$.each(file, function(prop) {
  					if (file[prop] != origin[prop]) {
  						changed.push(file)
  						return false;
  					}
  				});
  			}
  		});
  		
  		// parents of removed dirs mark as changed (required for tree correct work)
  		$.each(removed, function(i, hash) {
  			var file  = files[hash], 
  				phash = file.phash;
  
  			if (phash 
  			&& file.mime == 'directory' 
  			&& $.inArray(phash, removed) === -1 
  			&& raw[phash] 
  			&& !isChanged(phash)) {
  				changed.push(raw[phash]);
  			}
  		});
  		
  		return {
  			added   : added,
  			removed : removed,
  			changed : changed
  		};
  	};
  	
  	/**
  	 * Sync content
  	 * 
  	 * @return jQuery.Deferred
  	 */
  	this.sync = function(onlydir, polling) {
  		this.autoSync('stop');
  		var self  = this,
  			compare = function(){
  				var c = '', cnt = 0, mtime = 0;
  				if (onlydir && polling) {
  					$.each(files, function(h, f) {
  						if (f.phash && f.phash === onlydir) {
  							++cnt;
  							mtime = Math.max(mtime, f.ts);
  						}
  						c = cnt+':'+mtime;
  					});
  				}
  				return c;
  			},
  			comp  = compare(),
  			dfrd  = $.Deferred().done(function() { self.trigger('sync'); }),
  			opts1 = {
  				data           : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp},
  				preventDefault : true
  			},
  			opts2 = {
  				data           : {cmd : 'parents', target : cwd},
  				preventDefault : true
  			};
  		
  		$.when(
  			this.request(opts1),
  			onlydir? null : this.request(opts2)
  		)
  		.fail(function(error, xhr) {
  			if (! polling) {
  				dfrd.reject(error);
  				error && self.request({
  					data   : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  					notify : {type : 'open', cnt : 1, hideCnt : true}
  				});
  			} else {
  				dfrd.reject((error && xhr.status != 0)? error : void 0);
  			}
  		})
  		.done(function(odata, pdata) {
  			if (odata.cwd.compare) {
  				if (comp === odata.cwd.compare) {
  					return dfrd.reject();
  				}
  			}
  			
  			if (self.api < 2.1) {
  				pdata.tree = (pdata.tree || []).concat([odata.cwd]);
  			}
  			
  			var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir);
  
  			diff.added.push(odata.cwd)
  			diff.removed.length && self.remove(diff);
  			diff.added.length   && self.add(diff);
  			diff.changed.length && self.change(diff);
  			return dfrd.resolve(diff);
  		})
  		.always(function() {
  			self.autoSync();
  		});
  		
  		return dfrd;
  	};
  	
  	this.upload = function(files) {
  		return this.transport.upload(files, this);
  	};
  	
  	/**
  	 * Attach listener to events
  	 * To bind to multiply events at once, separate events names by space
  	 * 
  	 * @param  String  event(s) name(s)
  	 * @param  Object  event handler
  	 * @return elFinder
  	 */
  	this.bind = function(event, callback) {
  		var i;
  		
  		if (typeof(callback) == 'function') {
  			event = ('' + event).toLowerCase().split(/\s+/);
  			
  			for (i = 0; i < event.length; i++) {
  				if (listeners[event[i]] === void(0)) {
  					listeners[event[i]] = [];
  				}
  				listeners[event[i]].push(callback);
  			}
  		}
  		return this;
  	};
  	
  	/**
  	 * Remove event listener if exists
  	 * To un-bind to multiply events at once, separate events names by space
  	 *
  	 * @param  String    event(s) name(s)
  	 * @param  Function  callback
  	 * @return elFinder
  	 */
  	this.unbind = function(event, callback) {
  		var i, l, ci;
  		
  		event = ('' + event).toLowerCase().split(/\s+/);
  		
  		for (i = 0; i < event.length; i++) {
  			l = listeners[event[i]] || [];
  			ci = $.inArray(callback, l);
  			ci > -1 && l.splice(ci, 1);
  		}
  		
  		callback = null
  		return this;
  	};
  	
  	/**
  	 * Fire event - send notification to all event listeners
  	 *
  	 * @param  String   event type
  	 * @param  Object   data to send across event
  	 * @param  Boolean  allow modify data (call by reference of data)
  	 * @return elFinder
  	 */
  	this.trigger = function(event, data, allowModify) {
  		var event    = event.toLowerCase(),
  			isopen   = (event === 'open'),
  			handlers = listeners[event] || [], i, l, jst;
  		
  		this.debug('event-'+event, data);
  		
  		if (isopen && !allowModify) {
  			// for performance tuning
  			jst = JSON.stringify(data);
  		}
  		if (handlers.length) {
  			event = $.Event(event);
  			if (allowModify) {
  				event.data = data;
  			}
  
  			l = handlers.length;
  			for (i = 0; i < l; i++) {
  				// only callback has argument
  				if (handlers[i].length) {
  					if (!allowModify) {
  						// to avoid data modifications. remember about "sharing" passing arguments in js :) 
  						event.data = isopen? JSON.parse(jst) : $.extend(true, {}, data);
  					}
  				}
  
  				try {
  					if (handlers[i](event, this) === false 
  					|| event.isDefaultPrevented()) {
  						this.debug('event-stoped', event.type);
  						break;
  					}
  				} catch (ex) {
  					window.console && window.console.log && window.console.log(ex);
  				}
  				
  			}
  		}
  		return this;
  	};
  	
  	/**
  	 * Get event listeners
  	 *
  	 * @param  String   event type
  	 * @return Array    listed event functions
  	 */
  	this.getListeners = function(event) {
  		return event? listeners[event.toLowerCase()] : listeners;
  	};
  	
  	/**
  	 * Bind keybord shortcut to keydown event
  	 *
  	 * @example
  	 *    elfinder.shortcut({ 
  	 *       pattern : 'ctrl+a', 
  	 *       description : 'Select all files', 
  	 *       callback : function(e) { ... }, 
  	 *       keypress : true|false (bind to keypress instead of keydown) 
  	 *    })
  	 *
  	 * @param  Object  shortcut config
  	 * @return elFinder
  	 */
  	this.shortcut = function(s) {
  		var patterns, pattern, code, i, parts;
  		
  		if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) {
  			patterns = s.pattern.toUpperCase().split(/\s+/);
  			
  			for (i= 0; i < patterns.length; i++) {
  				pattern = patterns[i]
  				parts   = pattern.split('+');
  				code    = (code = parts.pop()).length == 1 
  					? code > 0 ? code : code.charCodeAt(0) 
  					: $.ui.keyCode[code];
  
  				if (code && !shortcuts[pattern]) {
  					shortcuts[pattern] = {
  						keyCode     : code,
  						altKey      : $.inArray('ALT', parts)   != -1,
  						ctrlKey     : $.inArray('CTRL', parts)  != -1,
  						shiftKey    : $.inArray('SHIFT', parts) != -1,
  						type        : s.type || 'keydown',
  						callback    : s.callback,
  						description : s.description,
  						pattern     : pattern
  					};
  				}
  			}
  		}
  		return this;
  	};
  	
  	/**
  	 * Registered shortcuts
  	 *
  	 * @type Object
  	 **/
  	this.shortcuts = function() {
  		var ret = [];
  		
  		$.each(shortcuts, function(i, s) {
  			ret.push([s.pattern, self.i18n(s.description)]);
  		});
  		return ret;
  	};
  	
  	/**
  	 * Get/set clipboard content.
  	 * Return new clipboard content.
  	 *
  	 * @example
  	 *   this.clipboard([]) - clean clipboard
  	 *   this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted
  	 * 
  	 * @param  Array    new files hashes
  	 * @param  Boolean  cut files?
  	 * @return Array
  	 */
  	this.clipboard = function(hashes, cut) {
  		var map = function() { return $.map(clipboard, function(f) { return f.hash }); }
  
  		if (hashes !== void(0)) {
  			clipboard.length && this.trigger('unlockfiles', {files : map()});
  			remember = [];
  			
  			clipboard = $.map(hashes||[], function(hash) {
  				var file = files[hash];
  				if (file) {
  					
  					remember.push(hash);
  					
  					return {
  						hash   : hash,
  						phash  : file.phash,
  						name   : file.name,
  						mime   : file.mime,
  						read   : file.read,
  						locked : file.locked,
  						cut    : !!cut
  					}
  				}
  				return null;
  			});
  			this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)});
  			cut && this.trigger('lockfiles', {files : map()});
  		}
  
  		// return copy of clipboard instead of refrence
  		return clipboard.slice(0, clipboard.length);
  	};
  	
  	/**
  	 * Return true if command enabled
  	 * 
  	 * @param  String       command name
  	 * @param  String|void  hash for check of own volume's disabled cmds
  	 * @return Boolean
  	 */
  	this.isCommandEnabled = function(name, dstHash) {
  		var disabled,
  			cvid = self.cwd().volumeid || '';
  		if (cvid && dstHash && dstHash.indexOf(cvid) !== 0) {
  			disabled = [];
  			$.each(self.disabledCmds, function(i, v){
  				if (dstHash.indexOf(i) === 0) {
  					disabled = v;
  					return false;
  				}
  			});
  		} else {
  			disabled = cwdOptions.disabled;
  		}
  		return this._commands[name] ? $.inArray(name, disabled) === -1 : false;
  	};
  	
  	/**
  	 * Exec command and return result;
  	 *
  	 * @param  String         command name
  	 * @param  String|Array   usualy files hashes
  	 * @param  String|Array   command options
  	 * @param  String|void    hash for enabled check of own volume's disabled cmds
  	 * @return $.Deferred
  	 */		
  	this.exec = function(cmd, files, opts, dstHash) {
  		if (cmd === 'open') {
  			if (this.searchStatus.state || this.searchStatus.ininc) {
  				this.trigger('searchend', { noupdate: true });
  			}
  			this.autoSync('stop');
  		}
  		return this._commands[cmd] && this.isCommandEnabled(cmd, dstHash) 
  			? this._commands[cmd].exec(files, opts) 
  			: $.Deferred().reject('No such command');
  	};
  	
  	/**
  	 * Create and return dialog.
  	 *
  	 * @param  String|DOMElement  dialog content
  	 * @param  Object             dialog options
  	 * @return jQuery
  	 */
  	this.dialog = function(content, options) {
  		var dialog = $('<div/>').append(content).appendTo(node).elfinderdialog(options),
  			dnode  = dialog.closest('.ui-dialog'),
  			resize = function(){
  				! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit');
  			};
  		if (dnode.length) {
  			self.bind('resize', resize);
  			dnode.on('remove', function() {
  				self.unbind('resize', resize);
  			});
  		}
  		return dialog;
  	};
  	
  	/**
  	 * Return UI widget or node
  	 *
  	 * @param  String  ui name
  	 * @return jQuery
  	 */
  	this.getUI = function(ui) {
  		return this.ui[ui] || node;
  	};
  	
  	this.command = function(name) {
  		return name === void(0) ? this._commands : this._commands[name];
  	};
  	
  	/**
  	 * Resize elfinder node
  	 * 
  	 * @param  String|Number  width
  	 * @param  Number         height
  	 * @return void
  	 */
  	this.resize = function(w, h) {
  		node.css('width', w).height(h).trigger('resize');
  		this.trigger('resize', {width : node.width(), height : node.height()});
  	};
  	
  	/**
  	 * Restore elfinder node size
  	 * 
  	 * @return elFinder
  	 */
  	this.restoreSize = function() {
  		this.resize(width, height);
  	};
  	
  	this.show = function() {
  		node.show();
  		this.enable().trigger('show');
  	};
  	
  	this.hide = function() {
  		this.disable().trigger('hide');
  		node.hide();
  	};
  	
  	/**
  	 * Lazy execution function
  	 * 
  	 * @param  Object  function
  	 * @param  Number  delay
  	 * @return Object  jQuery.Deferred
  	 */
  	this.lazy = function(func, delay) {
  		var busy = function(state) {
  				var cnt = node.data('lazycnt');
  				if (state) {
  					if (! cnt) {
  						node.data('lazycnt', 1);
  						node.addClass('elfinder-processing');
  					} else {
  						node.data('lazycnt', ++cnt);
  					}
  				} else {
  					if (cnt && cnt > 1) {
  						node.data('lazycnt', --cnt);
  					} else {
  						node.data('lazycnt', 0);
  						node.removeClass('elfinder-processing');
  					}
  				}
  			},
  			dfd  = $.Deferred();
  		
  		delay = delay || 0;
  		busy(true);
  		
  		// for node re-paint
  		node.outerHeight();
  		
  		setTimeout(function() {
  			func.call(dfd);
  			busy(false);
  		}, delay);
  		return dfd;
  	}
  	
  	/**
  	 * Destroy this elFinder instance
  	 *
  	 * @return void
  	 **/
  	this.destroy = function() {
  		if (node && node[0].elfinder) {
  			this.options.syncStart = false;
  			this.autoSync('stop');
  			this.trigger('destroy').disable();
  			clipboard = [];
  			selected = [];
  			listeners = {};
  			shortcuts = {};
  			$(window).off('.' + namespace);
  			$(document).add(node).off('.' + namespace);
  			self.trigger = function() { }
  			beeper.remove();
  			node.children().remove();
  			node.off();
  			node.append(prevContent.contents()).removeClass(this.cssClass).attr('style', prevStyle);
  			node[0].elfinder = null;
  		}
  	};
  	
  	/**
  	 * Start or stop auto sync
  	 * 
  	 * @param  String|Bool  stop
  	 * @return void
  	 */
  	this.autoSync = function(stop) {
  		var sync;
  		if (self.options.sync >= 1000) {
  			if (syncInterval) {
  				clearTimeout(syncInterval);
  				syncInterval = null;
  				self.trigger('autosync', {action : 'stop'});
  			}
  			if (stop || !self.options.syncStart) {
  				return;
  			}
  			// run interval sync
  			sync = function(start){
  				var timeout;
  				if (cwdOptions.syncMinMs && (start || syncInterval)) {
  					start && self.trigger('autosync', {action : 'start'});
  					timeout = Math.max(self.options.sync, cwdOptions.syncMinMs);
  					syncInterval && clearTimeout(syncInterval);
  					syncInterval = setTimeout(function() {
  						var dosync = true, hash = cwd, cts;
  						if (cwdOptions.syncChkAsTs && (cts = files[hash].ts)) {
  							self.request({
  								data           : {cmd : 'info', targets : [hash], compare : cts, reload : 1},
  								preventDefault : true
  							})
  							.done(function(data){
  								var ts;
  								dosync = true;
  								if (data.compare) {
  									ts = data.compare;
  									if (ts == cts) {
  										dosync = false;
  									}
  								}
  								if (dosync) {
  									self.sync(hash).always(function(){
  										if (ts) {
  											// update ts for cache clear etc.
  											files[hash].ts = ts;
  										}
  										sync();
  									});
  								} else {
  									sync();
  								}
  							})
  							.fail(function(error, xhr){
  								if (error && xhr.status != 0) {
  									self.error(error);
  								} else {
  									syncInterval = setTimeout(function() {
  										sync();
  									}, timeout);
  								}
  							});
  						} else {
  							self.sync(cwd, true).always(function(){
  								sync();
  							});
  						}
  					}, timeout);
  				}
  			};
  			sync(true);
  		}
  	};
  	
  	/**
  	 * Return bool is inside work zone of specific point
  	 * 
  	 * @param  Number event.pageX
  	 * @param  Number event.pageY
  	 * @return Bool
  	 */
  	this.insideWorkzone = function(x, y, margin) {
  		var rectangle = this.getUI('workzone').data('rectangle');
  		
  		margin = margin || 1;
  		if (x < rectangle.left + margin
  		|| x > rectangle.left + rectangle.width + margin
  		|| y < rectangle.top + margin
  		|| y > rectangle.top + rectangle.height + margin) {
  			return false;
  		}
  		return true;
  	};
  	
  	/*************  init stuffs  ****************/
  	
  	// check jquery ui
  	if (!($.fn.selectable && $.fn.draggable && $.fn.droppable)) {
  		return alert(this.i18n('errJqui'));
  	}
  
  	// check node
  	if (!node.length) {
  		return alert(this.i18n('errNode'));
  	}
  	// check connector url
  	if (!this.options.url) {
  		return alert(this.i18n('errURL'));
  	}
  
  	$.extend($.ui.keyCode, {
  		'F1' : 112,
  		'F2' : 113,
  		'F3' : 114,
  		'F4' : 115,
  		'F5' : 116,
  		'F6' : 117,
  		'F7' : 118,
  		'F8' : 119,
  		'F9' : 120
  	});
  	
  	this.dragUpload = false;
  	this.xhrUpload  = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined';
  	
  	// configure transport object
  	this.transport = {};
  
  	if (typeof(this.options.transport) == 'object') {
  		this.transport = this.options.transport;
  		if (typeof(this.transport.init) == 'function') {
  			this.transport.init(this)
  		}
  	}
  	
  	if (typeof(this.transport.send) != 'function') {
  		this.transport.send = function(opts) { return $.ajax(opts); }
  	}
  	
  	if (this.transport.upload == 'iframe') {
  		this.transport.upload = $.proxy(this.uploads.iframe, this);
  	} else if (typeof(this.transport.upload) == 'function') {
  		this.dragUpload = !!this.options.dragUploadAllow;
  	} else if (this.xhrUpload && !!this.options.dragUploadAllow) {
  		this.transport.upload = $.proxy(this.uploads.xhr, this);
  		this.dragUpload = true;
  	} else {
  		this.transport.upload = $.proxy(this.uploads.iframe, this);
  	}
  
  	/**
  	 * Alias for this.trigger('error', {error : 'message'})
  	 *
  	 * @param  String  error message
  	 * @return elFinder
  	 **/
  	this.error = function() {
  		var arg = arguments[0],
  			opts = arguments[1] || null;
  		return arguments.length == 1 && typeof(arg) == 'function'
  			? self.bind('error', arg)
  			: self.trigger('error', {error : arg, opts : opts});
  	};
  	
  	// create bind/trigger aliases for build-in events
  	$.each(['enable', 'disable', 'load', 'open', 'reload', 'select',  'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'], function(i, name) {
  		self[name] = function() {
  			var arg = arguments[0];
  			return arguments.length == 1 && typeof(arg) == 'function'
  				? self.bind(name, arg)
  				: self.trigger(name, $.isPlainObject(arg) ? arg : {});
  		}
  	});
  	
  	// bind core event handlers
  	this
  		.enable(function() {
  			if (!enabled && self.visible() && self.ui.overlay.is(':hidden')) {
  				enabled = true;
  				document.activeElement && document.activeElement.blur();
  				node.removeClass('elfinder-disabled');
  			}
  		})
  		.disable(function() {
  			prevEnabled = enabled;
  			enabled = false;
  			node.addClass('elfinder-disabled');
  		})
  		.open(function() {
  			selected = [];
  		})
  		.select(function(e) {
  			selected = $.map(e.data.selected || e.data.value|| [], function(hash) { return files[hash] ? hash : null; });
  		})
  		.error(function(e) { 
  			var opts  = {
  					cssClass  : 'elfinder-dialog-error',
  					title     : self.i18n(self.i18n('error')),
  					resizable : false,
  					destroyOnClose : true,
  					buttons   : {}
  			};
  			
  			opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); };
  
  			if (e.data.opts && $.isPlainObject(e.data.opts)) {
  				$.extend(opts, e.data.opts);
  			}
  
  			self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts);
  		})
  		.bind('tree parents', function(e) {
  			cache(e.data.tree || []);
  		})
  		.bind('tmb', function(e) {
  			$.each(e.data.images||[], function(hash, tmb) {
  				if (files[hash]) {
  					files[hash].tmb = tmb;
  				}
  			})
  		})
  		.add(function(e) {
  			cache(e.data.added||[]);
  		})
  		.change(function(e) {
  			$.each(e.data.changed||[], function(i, file) {
  				var hash = file.hash;
  				if (files[hash]) {
  					$.each(['locked', 'hidden', 'width', 'height'], function(i, v){
  						if (files[hash][v] && !file[v]) {
  							delete files[hash][v];
  						}
  					});
  				}
  				files[hash] = files[hash] ? $.extend(files[hash], file) : file;
  			});
  		})
  		.remove(function(e) {
  			var removed = e.data.removed||[],
  				l       = removed.length,
  				roots   = {},
  				rm      = function(hash) {
  					var file = files[hash], i;
  					if (file) {
  						if (file.mime === 'directory') {
  							if (roots[hash]) {
  								delete self.roots[roots[hash]];
  							}
  							$.each(files, function(h, f) {
  								f.phash == hash && rm(h);
  							});
  						}
  						delete files[hash];
  					}
  				};
  		
  			$.each(self.roots, function(k, v) {
  				roots[v] = k;
  			});
  			while (l--) {
  				rm(removed[l]);
  			}
  			
  		})
  		.bind('searchstart', function(e) {
  			$.extend(self.searchStatus, e.data);
  			self.searchStatus.state = 1;
  		})
  		.bind('search', function(e) {
  			self.searchStatus.state = 2;
  			cache(e.data.files);
  		})
  		.bind('searchend', function() {
  			self.searchStatus.state = 0;
  		})
  		.bind('rm', function(e) {
  			var play  = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"');
  		
  			play && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + 'rm.wav" type="audio/wav">')[0].play()
  		})
  		
  		;
  
  	// bind external event handlers
  	$.each(this.options.handlers, function(event, callback) {
  		self.bind(event, callback);
  	});
  
  	/**
  	 * History object. Store visited folders
  	 *
  	 * @type Object
  	 **/
  	this.history = new this.history(this);
  	
  	// in getFileCallback set - change default actions on double click/enter/ctrl+enter
  	if (this.commands.getfile) {
  		if (typeof(this.options.getFileCallback) == 'function') {
  			this.bind('dblclick', function(e) {
  				e.preventDefault();
  				self.exec('getfile').fail(function() {
  					self.exec('open');
  				});
  			});
  			this.shortcut({
  				pattern     : 'enter',
  				description : this.i18n('cmdgetfile'),
  				callback    : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open') }) }
  			})
  			.shortcut({
  				pattern     : 'ctrl+enter',
  				description : this.i18n(this.OS == 'mac' ? 'cmdrename' : 'cmdopen'),
  				callback    : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open') }
  			});
  		} else {
  			this.options.getFileCallback = null;
  		}
  	}
  
  	/**
  	 * Root hashed
  	 * 
  	 * @type Object
  	 */
  	this.roots = {};
  	
  	/**
  	 * Loaded commands
  	 *
  	 * @type Object
  	 **/
  	this._commands = {};
  	
  	if (!$.isArray(this.options.commands)) {
  		this.options.commands = [];
  	}
  	// check required commands
  	$.each(['open', 'reload', 'back', 'forward', 'up', 'home', 'info', 'quicklook', 'getfile', 'help'], function(i, cmd) {
  		var idx = $.inArray(cmd, self.options.commands);
  		if (cmd === 'getfile' && ! self.options.getFileCallback) {
  			idx !== -1 && self.options.commands.splice(idx, 1);
  			return true;
  		}
  		idx === -1 && self.options.commands.push(cmd);
  	});
  
  	// load commands
  	$.each(this.options.commands, function(i, name) {
  		var cmd = self.commands[name];
  		if ($.isFunction(cmd) && !self._commands[name]) {
  			cmd.prototype = base;
  			self._commands[name] = new cmd();
  			self._commands[name].setup(name, self.options.commandsOptions[name]||{});
  			// setup linked commands
  			if (self._commands[name].linkedCmds.length) {
  				$.each(self._commands[name].linkedCmds, function(i, n) {
  					var lcmd = self.commands[n];
  					if ($.isFunction(lcmd) && !self._commands[n]) {
  						lcmd.prototype = base;
  						self._commands[n] = new lcmd();
  						self._commands[n].setup(n, self.options.commandsOptions[n]||{});
  					}
  				});
  			}
  		}
  	});
  	
  	/**
  	 * UI command map of cwd volume ( That volume driver option `uiCmdMap` )
  	 *
  	 * @type Object
  	 **/
  	this.commandMap = {};
  	
  	/**
  	 * Disabled commands Array of each volume
  	 * 
  	 * @type Object
  	 */
  	this.disabledCmds = {};
  	
  	/**
  	 * tmbUrls Array of each volume
  	 * 
  	 * @type Object
  	 */
  	this.tmbUrls = {};
  	
  	// prepare node
  	node.addClass(this.cssClass)
  		.on(mousedown, function() {
  			!enabled && self.enable();
  		});
  	
  	/**
  	 * UI nodes
  	 *
  	 * @type Object
  	 **/
  	this.ui = {
  		// container for nav panel and current folder container
  		workzone : $('<div/>').appendTo(node).elfinderworkzone(this),
  		// container for folders tree / places
  		navbar : $('<div/>').appendTo(node).elfindernavbar(this, this.options.uiOptions.navbar || {}),
  		// contextmenu
  		contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(this),
  		// overlay
  		overlay : $('<div/>').appendTo(node).elfinderoverlay({
  			show : function() { self.disable(); },
  			hide : function() { prevEnabled && self.enable(); }
  		}),
  		// current folder container
  		cwd : $('<div/>').appendTo(node).elfindercwd(this, this.options.uiOptions.cwd || {}),
  		// notification dialog window
  		notify : this.dialog('', {
  			cssClass      : 'elfinder-dialog-notify',
  			position      : this.options.notifyDialog.position,
  			resizable     : false,
  			autoOpen      : false,
  			closeOnEscape : false,
  			title         : '&nbsp;',
  			width         : parseInt(this.options.notifyDialog.width)
  		}),
  		statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node)
  	};
  	
  	/**
  	 * UI Auto Hide Functions
  	 * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process
  	 *
  	 * @type Array
  	 **/
  	this.uiAutoHide = [];
  	
  	// trigger `uiautohide`
  	this.one('open', function() {
  		if (self.uiAutoHide.length) {
  			setTimeout(function() {
  				self.trigger('uiautohide');
  			}, 500);
  		}
  	});
  	
  	// Auto Hide Functions sequential processing start
  	this.bind('uiautohide', function() {
  		if (self.uiAutoHide.length) {
  			self.uiAutoHide.shift()();
  		}
  	});
  	
  	// load required ui
  	$.each(this.options.ui || [], function(i, ui) {
  		var name = 'elfinder'+ui,
  			opts = self.options.uiOptions[ui] || {};
  
  		if (!self.ui[ui] && $.fn[name]) {
  			// regist to self.ui before make instance
  			self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node);
  			self.ui[ui][name](self, opts);
  		}
  	});
  	
  
  
  	// store instance in node
  	node[0].elfinder = this;
  	
  	// make node resizable
  	this.options.resizable 
  	&& $.fn.resizable 
  	&& node.resizable({
  		handles   : 'se',
  		minWidth  : 300,
  		minHeight : 200
  	});
  
  	if (this.options.width) {
  		width = this.options.width;
  	}
  	
  	if (this.options.height) {
  		height = parseInt(this.options.height);
  	}
  	
  	if (this.options.soundPath) {
  		soundPath = this.options.soundPath.replace(/\/+$/, '') + '/';
  	}
  	
  	// update size	
  	self.resize(width, height);
  	
  	// attach events to document
  	$(document)
  		// disable elfinder on click outside elfinder
  		.on('click.'+namespace, function(e) { enabled && !$(e.target).closest(node).length && self.disable(); })
  		// exec shortcuts
  		.on(keydown+' '+keypress, execShortcut);
  	
  	// attach events to window
  	self.options.useBrowserHistory && $(window)
  		.on('popstate.' + namespace, function(ev) {
  			var target = ev.originalEvent.state && ev.originalEvent.state.thash;
  			target && !$.isEmptyObject(self.files()) && self.request({
  				data   : {cmd  : 'open', target : target, onhistory : 1},
  				notify : {type : 'open', cnt : 1, hideCnt : true},
  				syncOnFail : true
  			});
  		});
  	
  	// send initial request and start to pray >_<
  	this.trigger('init')
  		.request({
  			data        : {cmd : 'open', target : self.startDir(), init : 1, tree : this.ui.tree ? 1 : 0}, 
  			preventDone : true,
  			notify      : {type : 'open', cnt : 1, hideCnt : true},
  			freeze      : true
  		})
  		.fail(function() {
  			self.trigger('fail').disable().lastDir('');
  			listeners = {};
  			shortcuts = {};
  			$(document).add(node).off('.'+namespace);
  			self.trigger = function() { };
  		})
  		.done(function(data) {
  			// detect elFinder node z-index
  			var ni = node.css('z-index');
  			if (ni && ni !== 'auto' && ni !== 'inherit') {
  				self.zIndex = ni;
  			} else {
  				node.parents().each(function(i, n) {
  					var z = $(n).css('z-index');
  					if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) {
  						self.zIndex = z;
  						return false;
  					}
  				});
  			}
  			
  			self.load().debug('api', self.api);
  			data = $.extend(true, {}, data);
  			open(data);
  			self.trigger('open', data);
  		});
  	
  	// update ui's size after init
  	this.one('load', function() {
  		node.trigger('resize');
  	});
  
  	(function(){
  		var tm;
  		$(window).on('resize.' + namespace, function(){
  			tm && clearTimeout(tm);
  			tm = setTimeout(function() {
  				self.trigger('resize', {width : node.width(), height : node.height()});
  			}, 200);
  		})
  		.on('beforeunload.' + namespace,function(e){
  			var msg, cnt;
  			if (node.is(':visible')) {
  				if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) {
  					msg = self.i18n('ntfsmth');
  				} else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) {
  					msg = self.i18n('editingFile');
  				} else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) {
  					msg = self.i18n('hasSelected', ''+cnt);
  				} else if ((cnt = Object.keys(self.clipboard()).length) && $.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) {
  					msg = self.i18n('hasClipboard', ''+cnt);
  				}
  				if (msg) {
  					e.returnValue = msg;
  					return msg;
  				}
  			}
  			self.trigger('unload');
  		});
  	})();
  
  	// bind window onmessage for CORS
  	$(window).on('message.' + namespace, function(e){
  		var res = e.originalEvent || null,
  			obj, data;
  		if (res && self.uploadURL.indexOf(res.origin) === 0) {
  			try {
  				obj = JSON.parse(res.data);
  				data = obj.data || null;
  				if (data) {
  					if (data.error) {
  						self.error(data.error);
  					} else {
  						data.warning && self.error(data.warning);
  						data.removed && data.removed.length && self.remove(data);
  						data.added   && data.added.length   && self.add(data);
  						data.changed && data.changed.length && self.change(data);
  						if (obj.bind) {
  							self.trigger(obj.bind, data);
  						}
  						data.sync && self.sync();
  					}
  				}
  			} catch (e) {
  				self.sync();
  			}
  		}
  	});
  
  	// elFinder enable always
  	if (self.options.enableAlways) {
  		$(window).on('focus.' + namespace, function(e){
  			(e.target === this) && self.enable();
  		});
  	}
  
  	if (self.dragUpload) {
  		(function(){
  			var isin = function(e) {
  				return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0);
  			},
  			enter = function(e) {
  				if (isin(e)) {
  					e.preventDefault();
  					e.stopPropagation();
  				}
  			},
  			leave = function(e) {
  				if (isin(e)) {
  					e.preventDefault();
  					e.stopPropagation();
  				}
  			},
  			over = function(e) {
  				if (isin(e)) {
  					e.preventDefault();
  					e.stopPropagation();
  					e.dataTransfer.dropEffect = 'none';
  				}
  			},
  			drop =function(e) {
  				if (isin(e)) {
  					e.stopPropagation();
  					e.preventDefault();
  				}
  			};
  			node[0].addEventListener('dragenter', enter, false);
  			node[0].addEventListener('dragleave', leave, false);
  			node[0].addEventListener('dragover', over, false);
  			node[0].addEventListener('drop', drop, false);
  			self.bind('destroy', function() {
  				node[0].removeEventListener('dragenter', enter, false);
  				node[0].removeEventListener('dragleave', leave, false);
  				node[0].removeEventListener('dragover', over, false);
  				node[0].removeEventListener('drop', drop, false);
  			});
  		})();
  
  		// add event listener for HTML5 DnD upload
  		(function() {
  			var ent   = 'native-drag-enter',
  			disable   = 'native-drag-disable',
  			c         = 'class',
  			navdir    = self.res(c, 'navdir'),
  			droppable = self.res(c, 'droppable'),
  			collapsed = self.res(c, 'navcollapse'),
  			expanded  = self.res(c, 'navexpand'),
  			dropover  = self.res(c, 'adroppable'),
  			arrow     = self.res(c, 'navarrow'),
  			clDropActive = self.res(c, 'adroppable');
  			node.on('dragenter', '.native-droppable', function(e){
  				if (e.originalEvent.dataTransfer) {
  					var $elm = $(e.currentTarget),
  						id   = e.currentTarget.id || null,
  						cwd  = null,
  						elfFrom;
  					if (!id) { // target is cwd
  						cwd = self.cwd();
  						$elm.data(disable, false);
  						try {
  							$.each(e.originalEvent.dataTransfer.types, function(i, v){
  								if (v.substr(0, 13) === 'elfinderfrom:') {
  									elfFrom = v.substr(13).toLowerCase();
  								}
  							});
  						} catch(e) {}
  					} else {
  						if (!$elm.data(ent) && $elm.hasClass(navdir) && $elm.is('.'+collapsed+':not(.'+expanded+')')) {
  							setTimeout(function() {
  								$elm.is('.'+collapsed+'.'+dropover) && $elm.children('.'+arrow).click();
  							}, 500);
  						}
  					}
  					if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
  						e.preventDefault();
  						e.stopPropagation();
  						$elm.data(ent, true);
  						$elm.addClass(clDropActive);
  					} else {
  						$elm.data(disable, true);
  					}
  				}
  			})
  			.on('dragleave', '.native-droppable', function(e){
  				if (e.originalEvent.dataTransfer) {
  					var $elm = $(e.currentTarget);
  					e.preventDefault();
  					e.stopPropagation();
  					if ($elm.data(ent)) {
  						$elm.data(ent, false);
  					} else {
  						$elm.removeClass(clDropActive);
  					}
  				}
  			})
  			.on('dragover', '.native-droppable', function(e){
  				if (e.originalEvent.dataTransfer) {
  					var $elm = $(e.currentTarget);
  					e.preventDefault();
  					e.stopPropagation();
  					e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
  					$elm.data(ent, false);
  				}
  			})
  			.on('drop', '.native-droppable', function(e){
  				if (e.originalEvent.dataTransfer) {
  					var $elm = $(e.currentTarget)
  						id;
  					e.preventDefault();
  					e.stopPropagation();
  					$elm.removeClass(clDropActive);
  					if (e.currentTarget.id) {
  						id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
  					} else {
  						id = self.cwd().hash;
  					}
  					e.originalEvent._target = id;
  					self.directUploadTarget = id;
  					self.exec('upload', {dropEvt: e.originalEvent, target: id});
  					self.directUploadTarget = null;
  				}
  			});
  		})();
  	}
  
  	// Swipe on the touch devices to show/hide of toolbar or navbar
  	if (self.UA.Touch) {
  		(function() {
  			var lastX, lastY, nodeOffset, nodeTop, toolbarH,
  				navbar = self.getUI('navbar'),
  				toolbar = self.getUI('toolbar'),
  				moveOn  = function(e) {
  					e.preventDefault();
  				},
  				moveOff = function() {
  					$(document).off('touchmove', moveOn);
  				},
  				handleW, handleH = 50;
  
  			node.on('touchstart touchmove touchend', function(e) {
  				if (e.type === 'touchend') {
  					lastX = false;
  					lastY = false;
  					moveOff();
  					return;
  				}
  				
  				var touches = e.originalEvent.touches || [{}],
  					x = touches[0].pageX || null,
  					y = touches[0].pageY || null,
  					navbarMode;
  				
  				if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
  					return;
  				}
  				
  				if (e.type === 'touchstart') {
  					nodeOffset = node.offset();
  					if (navbar) {
  						lastX = false;
  						if (navbar.is(':hidden')) {
  							if (! handleW) {
  								handleW = Math.max(50, node.width() / 10)
  							}
  							if ((self.direction === 'ltr'? (x - nodeOffset.left) : (node.width() + nodeOffset.left - x)) < handleW) {
  								lastX = x;
  							}
  						} else {
  							handleW = Math.max(50, node.width() / 10);
  							lastX = x;
  						}
  					}
  					if (toolbar) {
  						toolbarH = toolbar.height();
  						nodeTop = nodeOffset.top;
  						if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
  							lastY = y;
  							$(document).on('touchmove.' + namespace, moveOn);
  							setTimeout(function() {
  								moveOff();
  							}, 500);
  						} else {
  							lastY = false;
  						}
  					}
  				} else {
  					if (navbar && lastX !== false) {
  						navbarMode = (self.direction === 'ltr'? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
  						if (Math.abs(lastX - x) > Math.min((navbarMode === 'navhide'? 200 : 45), (node.width() * .5))) {
  							self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
  							lastX = false;
  						}
  					}
  					if (toolbar && lastY !== false ) {
  						if (Math.abs(lastY - y) > toolbarH / 3) {
  							var mode = (lastY > y)? 'slideUp' : 'slideDown';
  							
  							if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
  								toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
  								moveOff();
  							}
  							lastY = false;
  						}
  					}
  				}
  			});
  		})();
  	}
  	
  	// self.timeEnd('load'); 
  
  }
  
  /**
   * Prototype
   * 
   * @type  Object
   */
  elFinder.prototype = {
  	
  	uniqueid : 0,
  	
  	res : function(type, id) {
  		return this.resources[type] && this.resources[type][id];
  	}, 
  	
  	/**
  	 * Current request command
  	 * 
  	 * @type  String
  	 */
  	currentReqCmd : '',
  	
  	/**
  	 * Internationalization object
  	 * 
  	 * @type  Object
  	 */
  	i18 : {
  		en : {
  			translator      : '',
  			language        : 'English',
  			direction       : 'ltr',
  			dateFormat      : 'd.m.Y H:i',
  			fancyDateFormat : '$1 H:i',
  			messages        : {}
  		},
  		months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  		monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
  
  		days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  		daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  	},
  	
  	/**
  	 * File mimetype to kind mapping
  	 * 
  	 * @type  Object
  	 */
  	kinds : 	{
  			'unknown'                       : 'Unknown',
  			'directory'                     : 'Folder',
  			'symlink'                       : 'Alias',
  			'symlink-broken'                : 'AliasBroken',
  			'application/x-empty'           : 'TextPlain',
  			'application/postscript'        : 'Postscript',
  			'application/vnd.ms-office'     : 'MsOffice',
  			'application/msword'            : 'MsWord',
  			'application/vnd.ms-word'       : 'MsWord',
  			'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
  			'application/vnd.ms-word.document.macroEnabled.12'                        : 'MsWord',
  			'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
  			'application/vnd.ms-word.template.macroEnabled.12'                        : 'MsWord',
  			'application/vnd.ms-excel'      : 'MsExcel',
  			'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'       : 'MsExcel',
  			'application/vnd.ms-excel.sheet.macroEnabled.12'                          : 'MsExcel',
  			'application/vnd.openxmlformats-officedocument.spreadsheetml.template'    : 'MsExcel',
  			'application/vnd.ms-excel.template.macroEnabled.12'                       : 'MsExcel',
  			'application/vnd.ms-excel.sheet.binary.macroEnabled.12'                   : 'MsExcel',
  			'application/vnd.ms-excel.addin.macroEnabled.12'                          : 'MsExcel',
  			'application/vnd.ms-powerpoint' : 'MsPP',
  			'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
  			'application/vnd.ms-powerpoint.presentation.macroEnabled.12'              : 'MsPP',
  			'application/vnd.openxmlformats-officedocument.presentationml.slideshow'  : 'MsPP',
  			'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'                 : 'MsPP',
  			'application/vnd.openxmlformats-officedocument.presentationml.template'   : 'MsPP',
  			'application/vnd.ms-powerpoint.template.macroEnabled.12'                  : 'MsPP',
  			'application/vnd.ms-powerpoint.addin.macroEnabled.12'                     : 'MsPP',
  			'application/vnd.openxmlformats-officedocument.presentationml.slide'      : 'MsPP',
  			'application/vnd.ms-powerpoint.slide.macroEnabled.12'                     : 'MsPP',
  			'application/pdf'               : 'PDF',
  			'application/xml'               : 'XML',
  			'application/vnd.oasis.opendocument.text' : 'OO',
  			'application/vnd.oasis.opendocument.text-template'         : 'OO',
  			'application/vnd.oasis.opendocument.text-web'              : 'OO',
  			'application/vnd.oasis.opendocument.text-master'           : 'OO',
  			'application/vnd.oasis.opendocument.graphics'              : 'OO',
  			'application/vnd.oasis.opendocument.graphics-template'     : 'OO',
  			'application/vnd.oasis.opendocument.presentation'          : 'OO',
  			'application/vnd.oasis.opendocument.presentation-template' : 'OO',
  			'application/vnd.oasis.opendocument.spreadsheet'           : 'OO',
  			'application/vnd.oasis.opendocument.spreadsheet-template'  : 'OO',
  			'application/vnd.oasis.opendocument.chart'                 : 'OO',
  			'application/vnd.oasis.opendocument.formula'               : 'OO',
  			'application/vnd.oasis.opendocument.database'              : 'OO',
  			'application/vnd.oasis.opendocument.image'                 : 'OO',
  			'application/vnd.openofficeorg.extension'                  : 'OO',
  			'application/x-shockwave-flash' : 'AppFlash',
  			'application/flash-video'       : 'Flash video',
  			'application/x-bittorrent'      : 'Torrent',
  			'application/javascript'        : 'JS',
  			'application/rtf'               : 'RTF',
  			'application/rtfd'              : 'RTF',
  			'application/x-font-ttf'        : 'TTF',
  			'application/x-font-otf'        : 'OTF',
  			'application/x-rpm'             : 'RPM',
  			'application/x-web-config'      : 'TextPlain',
  			'application/xhtml+xml'         : 'HTML',
  			'application/docbook+xml'       : 'DOCBOOK',
  			'application/x-awk'             : 'AWK',
  			'application/x-gzip'            : 'GZIP',
  			'application/x-bzip2'           : 'BZIP',
  			'application/x-xz'              : 'XZ',
  			'application/zip'               : 'ZIP',
  			'application/x-zip'               : 'ZIP',
  			'application/x-rar'             : 'RAR',
  			'application/x-tar'             : 'TAR',
  			'application/x-7z-compressed'   : '7z',
  			'application/x-jar'             : 'JAR',
  			'text/plain'                    : 'TextPlain',
  			'text/x-php'                    : 'PHP',
  			'text/html'                     : 'HTML',
  			'text/javascript'               : 'JS',
  			'text/css'                      : 'CSS',
  			'text/rtf'                      : 'RTF',
  			'text/rtfd'                     : 'RTF',
  			'text/x-c'                      : 'C',
  			'text/x-csrc'                   : 'C',
  			'text/x-chdr'                   : 'CHeader',
  			'text/x-c++'                    : 'CPP',
  			'text/x-c++src'                 : 'CPP',
  			'text/x-c++hdr'                 : 'CPPHeader',
  			'text/x-shellscript'            : 'Shell',
  			'application/x-csh'             : 'Shell',
  			'text/x-python'                 : 'Python',
  			'text/x-java'                   : 'Java',
  			'text/x-java-source'            : 'Java',
  			'text/x-ruby'                   : 'Ruby',
  			'text/x-perl'                   : 'Perl',
  			'text/x-sql'                    : 'SQL',
  			'text/xml'                      : 'XML',
  			'text/x-comma-separated-values' : 'CSV',
  			'text/x-markdown'               : 'Markdown',
  			'image/x-ms-bmp'                : 'BMP',
  			'image/jpeg'                    : 'JPEG',
  			'image/gif'                     : 'GIF',
  			'image/png'                     : 'PNG',
  			'image/tiff'                    : 'TIFF',
  			'image/x-targa'                 : 'TGA',
  			'image/vnd.adobe.photoshop'     : 'PSD',
  			'image/xbm'                     : 'XBITMAP',
  			'image/pxm'                     : 'PXM',
  			'audio/mpeg'                    : 'AudioMPEG',
  			'audio/midi'                    : 'AudioMIDI',
  			'audio/ogg'                     : 'AudioOGG',
  			'audio/mp4'                     : 'AudioMPEG4',
  			'audio/x-m4a'                   : 'AudioMPEG4',
  			'audio/wav'                     : 'AudioWAV',
  			'audio/x-mp3-playlist'          : 'AudioPlaylist',
  			'video/x-dv'                    : 'VideoDV',
  			'video/mp4'                     : 'VideoMPEG4',
  			'video/mpeg'                    : 'VideoMPEG',
  			'video/x-msvideo'               : 'VideoAVI',
  			'video/quicktime'               : 'VideoMOV',
  			'video/x-ms-wmv'                : 'VideoWM',
  			'video/x-flv'                   : 'VideoFlash',
  			'video/x-matroska'              : 'VideoMKV',
  			'video/ogg'                     : 'VideoOGG'
  		},
  	
  	/**
  	 * Ajax request data validation rules
  	 * 
  	 * @type  Object
  	 */
  	rules : {
  		defaults : function(data) {
  			if (!data
  			|| (data.added && !$.isArray(data.added))
  			||  (data.removed && !$.isArray(data.removed))
  			||  (data.changed && !$.isArray(data.changed))) {
  				return false;
  			}
  			return true;
  		},
  		open    : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && $.isArray(data.files); },
  		tree    : function(data) { return data && data.tree && $.isArray(data.tree); },
  		parents : function(data) { return data && data.tree && $.isArray(data.tree); },
  		tmb     : function(data) { return data && data.images && ($.isPlainObject(data.images) || $.isArray(data.images)); },
  		upload  : function(data) { return data && ($.isPlainObject(data.added) || $.isArray(data.added));},
  		search  : function(data) { return data && data.files && $.isArray(data.files)}
  	},
  	
  	/**
  	 * Commands costructors
  	 *
  	 * @type Object
  	 */
  	commands : {},
  	
  	/**
  	 * Commands to add the item (space delimited)
  	 * 
  	 * @type String
  	 */
  	cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
  	
  	parseUploadData : function(text) {
  		var data;
  		
  		if (!$.trim(text)) {
  			return {error : ['errResponse', 'errDataEmpty']};
  		}
  		
  		try {
  			data = $.parseJSON(text);
  		} catch (e) {
  			return {error : ['errResponse', 'errDataNotJSON']};
  		}
  		
  		if (!this.validResponse('upload', data)) {
  			return {error : ['errResponse']};
  		}
  		data = this.normalize(data);
  		data.removed = $.merge((data.removed || []), $.map(data.added||[], function(f) { return f.hash; }));
  		return data;
  		
  	},
  	
  	iframeCnt : 0,
  	
  	uploads : {
  		// xhr muiti uploading flag
  		xhrUploading: false,
  		
  		// check file/dir exists
  		checkExists: function(files, target, fm) {
  			var dfrd = $.Deferred(),
  				names, name,
  				cancel = function() {
  					var i = files.length;
  					while (--i > -1) {
  						files[i]._remove = true;
  					}
  				},
  				check = function() {
  					var renames = [], hashes = {}, existed = [], exists = [], i, c;
  					
  					var confirm = function(ndx) {
  						var last = ndx == exists.length-1,
  						opts = {
  							title  : fm.i18n('cmdupload'),
  							text   : ['errExists', exists[ndx].name, 'confirmRepl'], 
  							all    : !last,
  							accept : {
  								label    : 'btnYes',
  								callback : function(all) {
  									!last && !all
  										? confirm(++ndx)
  										: dfrd.resolve(renames, hashes);
  								}
  							},
  							reject : {
  								label    : 'btnNo',
  								callback : function(all) {
  									var i;
  	
  									if (all) {
  										i = exists.length;
  										while (ndx < i--) {
  											files[exists[i].i]._remove = true;
  										}
  									} else {
  										files[exists[ndx].i]._remove = true;
  									}
  	
  									!last && !all
  										? confirm(++ndx)
  										: dfrd.resolve(renames, hashes);
  								}
  							},
  							cancel : {
  								label    : 'btnCancel',
  								callback : function() {
  									cancel();
  									dfrd.resolve(renames, hashes);
  								}
  							},
  							buttons : [
  								{
  									label : 'btnBackup',
  									callback : function(all) {
  										var i;
  										if (all) {
  											i = exists.length;
  											while (ndx < i--) {
  												renames.push(exists[i].name);
  											}
  										} else {
  											renames.push(exists[ndx].name);
  										}
  										!last && !all
  											? confirm(++ndx)
  											: dfrd.resolve(renames, hashes);
  									}
  								}
  							]
  						};
  						if (fm.iframeCnt > 0) {
  							delete opts.reject;
  						}
  						fm.confirm(opts);
  					};
  					
  					names = $.map(files, function(file, i) { return file.name? {i: i, name: file.name} : null ;});
  					
  					name = $.map(names, function(item) { return item.name;});
  					fm.request({
  						data : {cmd : 'ls', target : target, intersect : name},
  						notify : {type : 'preupload', cnt : 1, hideCnt : true},
  						preventFail : true
  					})
  					.done(function(data) {
  						var existedArr, cwdItems;
  						if (data) {
  							if (data.error) {
  								cancel();
  							} else {
  								if (fm.option('uploadOverwrite') && ! fm.UA.iOS) {
  									if (data.list) {
  										if ($.isArray(data.list)) {
  											existed = data.list || [];
  										} else {
  											existedArr = [];
  											existed = $.map(data.list, function(n) {
  												if (typeof n === 'string') {
  													return n;
  												} else {
  													// support to >=2.1.11 plugin Normalizer, Sanitizer
  													existedArr = existedArr.concat(n);
  													return null;
  												}
  											});
  											if (existedArr.length) {
  												existed = existed.concat(existedArr);
  											}
  											hashes = data.list;
  										}
  										exists = $.map(names, function(name){
  											return $.inArray(name.name, existed) !== -1 ? name : null ;
  										});
  										if (existed.length && target == fm.cwd().hash) {
  											cwdItems = $.map(fm.files(), function(file) { return (file.phash == target) ? file.name : null; } );
  											if ($.map(existed, function(n) { 
  												return cwdItems.indexOf(n) === -1? true : null;
  											}).length){
  												fm.sync();
  											}
  										}
  									}
  								}
  							}
  						}
  						if (exists.length > 0) {
  							confirm(0);
  						} else {
  							dfrd.resolve([]);
  						}
  					})
  					.fail(function(error) {
  						cancel();
  						dfrd.resolve([]);
  						error && fm.error(error);
  					});
  				};
  			if (fm.api >= 2.1 && typeof files[0] == 'object') {
  				check();
  				return dfrd;
  			} else {
  				return dfrd.resolve([]);
  			}
  		},
  		
  		// check droped contents
  		checkFile : function(data, fm, target) {
  			if (!!data.checked || data.type == 'files') {
  				return data.files;
  			} else if (data.type == 'data') {
  				var dfrd = $.Deferred(),
  				files = [],
  				paths = [],
  				dirctorys = [],
  				entries = [],
  				processing = 0,
  				items,
  				mkdirs = [],
  				
  				readEntries = function(dirReader) {
  					var toArray = function(list) {
  						return Array.prototype.slice.call(list || []);
  					};
  				},
  				
  				doScan = function(items) {
  					var dirReader, entry,
  					entries = [],
  					toArray = function(list) {
  						return Array.prototype.slice.call(list || [], 0);
  					};
  					var length = items.length;
  					for (var i = 0; i < length; i++) {
  						entry = items[i];
  						if (entry) {
  							if (entry.isFile) {
  								processing++;
  								entry.file(function (file) {
  									if (! (fm.OS == 'win' && file.name.match(/^(?:desktop\.ini|thumbs\.db)$/i))
  											&&
  										! (fm.OS == 'mac' && file.name.match(/^\.ds_store$/i))) {
  										paths.push(entry.fullPath || '');
  										files.push(file);
  									}
  									processing--;
  								});
  							} else if (entry.isDirectory) {
  								if (fm.api >= 2.1) {
  									processing++;
  									mkdirs.push(entry.fullPath);
  									dirReader = entry.createReader();
  									var entries = [];
  									// Call the reader.readEntries() until no more results are returned.
  									var readEntries = function() {
  										 dirReader.readEntries (function(results) {
  											if (!results.length) {
  												for (var i = 0; i < entries.length; i++) {
  													doScan([entries[i]]);
  												}
  												processing--;
  											} else {
  												entries = entries.concat(toArray(results));
  												readEntries();
  											}
  										}, function(){
  											processing--;
  										});
  									};
  									readEntries(); // Start reading dirs.
  								}
  							}
  						}
  					}
  				};
  				
  				items = $.map(data.files.items, function(item){
  					return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
  				});
  				if (items.length > 0) {
  					fm.uploads.checkExists(items, target, fm).done(function(renames, hashes){
  						var notifyto, dfds = [];
  						if (fm.option('uploadOverwrite') && ! fm.UA.iOS) {
  							items = $.map(items, function(item){
  								var i, bak, hash, dfd, hi;
  								if (item.isDirectory) {
  									i = $.inArray(item.name, renames);
  									if (i !== -1) {
  										renames.splice(i, 1);
  										bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
  										$.each(hashes, function(h, name) {
  											if (item.name == name) {
  												hash = h;
  												return false;
  											}
  										});
  										if (! hash) {
  											hash = fm.fileByName(item.name, target).hash;
  										}
  										fm.lockfiles({files : [hash]});
  										dfd = fm.request({
  											data   : {cmd : 'rename', target : hash, name : bak},
  											notify : {type : 'rename', cnt : 1}
  										})
  										.fail(function(error) {
  											item._remove = true;
  											fm.sync();
  										})
  										.always(function() {
  											fm.unlockfiles({files : [hash]})
  										});
  										dfds.push(dfd);
  									}
  								}
  								return !item._remove? item : null;
  							});
  						}
  						$.when.apply($, dfds).done(function(){
  							if (items.length > 0) {
  								notifyto = setTimeout(function() {
  									fm.notify({type : 'readdir', cnt : 1, hideCnt: true});
  								}, fm.options.notifyDelay);
  								doScan(items);
  								setTimeout(function wait() {
  									if (processing > 0) {
  										setTimeout(wait, 10);
  									} else {
  										notifyto && clearTimeout(notifyto);
  										fm.notify({type : 'readdir', cnt : -1});
  										dfrd.resolve([files, paths, renames, hashes, mkdirs]);
  									}
  								}, 10);
  							} else {
  								dfrd.reject();
  							}
  						});
  					});
  					return dfrd.promise();
  				} else {
  					return dfrd.reject();
  				}
  			} else {
  				var ret = [];
  				var check = [];
  				var str = data.files[0];
  				if (data.type == 'html') {
  					var tmp = $("<html/>").append($.parseHTML(str)),
  						atag;
  					$('img[src]', tmp).each(function(){
  						var url, purl,
  						self = $(this),
  						pa = self.closest('a');
  						if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
  							purl = pa.attr('href');
  						}
  						url = self.attr('src');
  						if (url) {
  							if (purl) {
  								$.inArray(purl, ret) == -1 && ret.push(purl);
  								$.inArray(url, check) == -1 &&  check.push(url);
  							} else {
  								$.inArray(url, ret) == -1 && ret.push(url);
  							}
  						}
  					});
  					atag = $('a[href]', tmp);
  					atag.each(function(){
  						var loc,
  							parseUrl = function(url) {
  							    var a = document.createElement('a');
  							    a.href = url;
  							    return a;
  							};
  						if ($(this).text()) {
  							loc = parseUrl($(this).attr('href'));
  							if (loc.href && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i))) {
  								if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href);
  							}
  						}
  					});
  				} else {
  					var regex, m, url;
  					regex = /(http[^<>"{}|\\^\[\]`\s]+)/ig;
  					while (m = regex.exec(str)) {
  						url = m[1].replace(/&amp;/g, '&');
  						if ($.inArray(url, ret) == -1) ret.push(url);
  					}
  				}
  				return ret;
  			}
  		},
  
  		// upload transport using XMLHttpRequest
  		xhr : function(data, fm) { 
  			var self   = fm ? fm : this,
  				node        = self.getUI(),
  				xhr         = new XMLHttpRequest(),
  				notifyto    = null, notifyto2 = null,
  				dataChecked = data.checked,
  				isDataType  = (data.isDataType || data.type == 'data'),
  				chunkEnable = (self.option('uploadMaxConn') != -1),
  				multiMax    = Math.min(5, Math.max(1, self.option('uploadMaxConn'))),
  				retry       = 0,
  				dfrd   = $.Deferred()
  					.fail(function(error) {
  						if (self.uploads.xhrUploading) {
  							setTimeout(function() { self.sync(); }, 5000);
  							self.uploads.xhrUploading = false;
  							var file = files.length? (isDataType? files[0][0] : files[0]) : {};
  							if (file._cid) {
  								formData = new FormData();
  								files = [{_chunkfail: true}];
  								formData.append('chunk', file._chunk);
  								formData.append('cid'  , file._cid);
  								isDataType = false;
  								send(files);
  							}
  						}
  						files = null;
  						error && self.error(error);
  					})
  					.done(function(data) {
  						xhr = null;
  						files = null;
  						if (data) {
  							data.warning && self.error(data.warning);
  							data.removed && self.remove(data);
  							data.added   && self.add(data);
  							data.changed && self.change(data);
  		 					self.trigger('upload', data);
  							data.sync && self.sync();
  						}
  					})
  					.always(function() {
  						// unregist fnAbort function
  						node.off('uploadabort', fnAbort);
  						$(window).off('unload', fnAbort);
  						notifyto && clearTimeout(notifyto);
  						notifyto2 && clearTimeout(notifyto2);
  						dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  						chunkMerge && self.ui.notify.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
  					}),
  				formData    = new FormData(),
  				target      = (data.target || self.cwd().hash),
  				files       = data.input ? data.input.files : self.uploads.checkFile(data, self, target), 
  				cnt         = data.checked? (isDataType? files[0].length : files.length) : files.length,
  				loaded      = 0, prev,
  				filesize    = 0,
  				notify      = false,
  				abort       = false,
  				checkNotify = function() {
  					return notify = (notify || self.ui.notify.children('.elfinder-notify-upload').length);
  				},
  				fnAbort     = function() {
  					abort = true;
  					if (xhr) {
  						xhr.quiet = true;
  						xhr.abort();
  					}
  				},
  				startNotify = function(size) {
  					if (!size) size = filesize;
  					return setTimeout(function() {
  						notify = true;
  						self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
  							cancel: function() {
  								node.trigger('uploadabort');
  							}
  						});
  						prev = loaded;
  					}, self.options.notifyDelay);
  				},
  				renames = (data.renames || null),
  				hashes = (data.hashes || null),
  				chunkMerge = false;
  			
  			// regist fnAbort function
  			node.one('uploadabort', fnAbort);
  			$(window).one('unload.' + fm.namespace, fnAbort);
  			
  			!chunkMerge && (prev = loaded);
  			
  			if (!isDataType && !cnt) {
  				return dfrd.reject(['errUploadNoFiles']);
  			}
  			
  			xhr.addEventListener('error', function() {
  				dfrd.reject('errConnect');
  			}, false);
  			
  			xhr.addEventListener('abort', function() {
  				dfrd.reject(['errConnect', 'errAbort']);
  			}, false);
  			
  			xhr.addEventListener('load', function(e) {
  				var status = xhr.status, res, curr = 0, error = '';
  				
  				if (status >= 400) {
  					if (status > 500) {
  						error = 'errResponse';
  					} else {
  						error = 'errConnect';
  					}
  				} else {
  					if (xhr.readyState != 4) {
  						error = ['errConnect', 'errTimeout']; // am i right?
  					}
  					if (!xhr.responseText) {
  						error = ['errResponse', 'errDataEmpty'];
  					}
  				}
  				
  				if (error) {
  					if (chunkMerge || retry++ > 3) {
  						var file = isDataType? files[0][0] : files[0];
  						return dfrd.reject(file._cid? null : error);
  					} else {
  						// do retry
  						filesize = 0;
  						xhr.open('POST', self.uploadURL, true);
  						xhr.send(formData);
  						return;
  					}
  				}
  				
  				loaded = filesize;
  				
  				if (checkNotify() && (curr = loaded - prev)) {
  					self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  				}
  
  				res = self.parseUploadData(xhr.responseText);
  				
  				// chunked upload commit
  				if (res._chunkmerged) {
  					formData = new FormData();
  					var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
  					chunkMerge = true;
  					notifyto2 = setTimeout(function() {
  						self.notify({type : 'chunkmerge', cnt : 1});
  					}, self.options.notifyDelay);
  					isDataType? send(_file, files[1]) : send(_file);
  					return;
  				}
  				
  				res._multiupload = data.multiupload? true : false;
  				if (res.error) {
  					if (res._chunkfailure) {
  						abort = true;
  						self.uploads.xhrUploading = false;
  						notifyto && clearTimeout(notifyto);
  						if (self.ui.notify.children('.elfinder-notify-upload').length) {
  							self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  							dfrd.reject(res.error);
  						} else {
  							// for multi connection
  							dfrd.reject();
  						}
  					} else {
  						dfrd.reject(res.error);
  					}
  				} else {
  					dfrd.resolve(res);
  				}
  			}, false);
  			
  			xhr.upload.addEventListener('loadstart', function(e) {
  				if (!chunkMerge && e.lengthComputable) {
  					loaded = e.loaded;
  					retry && (loaded = 0);
  					filesize = e.total;
  					if (!loaded) {
  						loaded = parseInt(filesize * 0.05);
  					}
  					if (checkNotify()) {
  						self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
  						prev = loaded;
  					}
  				}
  			}, false);
  			
  			xhr.upload.addEventListener('progress', function(e) {
  				var curr;
  
  				if (e.lengthComputable && !chunkMerge) {
  					
  					loaded = e.loaded;
  
  					// to avoid strange bug in safari (not in chrome) with drag&drop.
  					// bug: macos finder opened in any folder,
  					// reset safari cache (option+command+e), reload elfinder page,
  					// drop file from finder
  					// on first attempt request starts (progress callback called ones) but never ends.
  					// any next drop - successfull.
  					if (!data.checked && loaded > 0 && !notifyto) {
  						notifyto = startNotify(xhr._totalSize - loaded);
  					}
  					
  					if (!filesize) {
  						retry && (loaded = 0);
  						filesize = e.total;
  						if (!loaded) {
  							loaded = parseInt(filesize * 0.05);
  						}
  					}
  					
  					curr = loaded - prev;
  					if (checkNotify() && (curr/e.total) >= 0.05) {
  						self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  						prev = loaded;
  					}
  				}
  			}, false);
  			
  			var send = function(files, paths){
  				var size = 0,
  				fcnt = 1,
  				sfiles = [],
  				c = 0,
  				total = cnt,
  				maxFileSize,
  				totalSize = 0,
  				chunked = [],
  				chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
  				BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
  				blobSlice = chunkEnable? false : '',
  				blobSize, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
  				multi = function(files, num){
  					var sfiles = [], cid;
  					if (!abort) {
  						while(files.length && sfiles.length < num) {
  							sfiles.push(files.shift());
  						}
  						if (sfiles.length) {
  							for (var i=0; i < sfiles.length; i++) {
  								if (abort) {
  									break;
  								}
  								cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
  								if (!!failChunk[cid]) {
  									last--;
  									continue;
  								}
  								fm.exec('upload', {
  									type: data.type,
  									isDataType: isDataType,
  									files: sfiles[i],
  									checked: true,
  									target: target,
  									renames: renames,
  									hashes: hashes,
  									multiupload: true})
  								.fail(function(error) {
  									if (cid) {	
  										failChunk[cid] = true;
  									}
  								})
  								.always(function(e) {
  									if (e && e.added) added = $.merge(added, e.added);
  									if (last <= ++done) {
  										fm.trigger('multiupload', {added: added});
  										notifyto && clearTimeout(notifyto);
  										if (checkNotify()) {
  											self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  										}
  									}
  									multi(files, 1); // Next one
  								});
  							}
  						}
  					}
  					if (sfiles.length < 1 || abort) {
  						if (abort) {
  							notifyto && clearTimeout(notifyto);
  							if (cid) {
  								failChunk[cid] = true;
  							}
  							dfrd.reject();
  						} else {
  							dfrd.resolve();
  							self.uploads.xhrUploading = false;
  						}
  					}
  				},
  				check = function(){
  					if (!self.uploads.xhrUploading) {
  						self.uploads.xhrUploading = true;
  						multi(sfiles, multiMax); // Max connection: 3
  					} else {
  						setTimeout(function(){ check(); }, 100);
  					}
  				};
  
  				if (! dataChecked && (isDataType || data.type == 'files')) {
  					maxFileSize = fm.option('uploadMaxSize')? fm.option('uploadMaxSize') : 0;
  					for (i=0; i < files.length; i++) {
  						blob = files[i];
  						blobSize = blob.size;
  						if (blobSlice === false) {
  							blobSlice = '';
  							if (self.api >= 2.1) {
  								if ('slice' in blob) {
  									blobSlice = 'slice';
  								} else if ('mozSlice' in blob) {
  									blobSlice = 'mozSlice';
  								} else if ('webkitSlice' in blob) {
  									blobSlice = 'webkitSlice';
  								}
  							}
  						}
  						
  						if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
  							self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize'));
  							cnt--;
  							total--;
  							continue;
  						}
  						
  						if (blobSlice && blobSize > BYTES_PER_CHUNK) {
  							start = 0;
  							end = BYTES_PER_CHUNK;
  							chunks = -1;
  							total = Math.floor(blobSize / BYTES_PER_CHUNK);
  
  							totalSize += blobSize;
  							chunked[chunkID] = 0;
  							while(start <= blobSize) {
  								chunk = blob[blobSlice](start, end);
  								chunk._chunk = blob.name + '.' + (++chunks) + '_' + total + '.part';
  								chunk._cid   = chunkID;
  								chunk._range = start + ',' + chunk.size + ',' + blobSize;
  								chunked[chunkID]++;
  								
  								if (size) {
  									c++;
  								}
  								if (typeof sfiles[c] == 'undefined') {
  									sfiles[c] = [];
  									if (isDataType) {
  										sfiles[c][0] = [];
  										sfiles[c][1] = [];
  									}
  								}
  								size = BYTES_PER_CHUNK;
  								fcnt = 1;
  								if (isDataType) {
  									sfiles[c][0].push(chunk);
  									sfiles[c][1].push(paths[i]);
  								} else {
  									sfiles[c].push(chunk);
  								}
  
  								start = end;
  								end = start + BYTES_PER_CHUNK;
  							}
  							if (chunk == null) {
  								self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize'));
  								cnt--;
  								total--;
  							} else {
  								total += chunks;
  							}
  							continue;
  						}
  						if ((fm.uplMaxSize && size + blobSize >= fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
  							size = 0;
  							fcnt = 1;
  							c++;
  						}
  						if (typeof sfiles[c] == 'undefined') {
  							sfiles[c] = [];
  							if (isDataType) {
  								sfiles[c][0] = [];
  								sfiles[c][1] = [];
  							}
  						}
  						if (isDataType) {
  							sfiles[c][0].push(blob);
  							sfiles[c][1].push(paths[i]);
  						} else {
  							sfiles[c].push(blob);
  						}
  						size += blobSize;
  						totalSize += blobSize;
  						fcnt++;
  					}
  					
  					if (sfiles.length == 0) {
  						// no data
  						data.checked = true;
  						return false;
  					}
  					
  					if (sfiles.length > 1) {
  						// multi upload
  						notifyto = startNotify(totalSize);
  						added = [];
  						done = 0;
  						last = sfiles.length;
  						failChunk = [];
  						check();
  						return true;
  					}
  					
  					// single upload
  					if (isDataType) {
  						files = sfiles[0][0];
  						paths = sfiles[0][1];
  					} else {
  						files = sfiles[0];
  					}
  				}
  				
  				if (!dataChecked) {
  					if (!fm.UA.Safari || !data.files) {
  						notifyto = startNotify(totalSize);
  					} else {
  						xhr._totalSize = totalSize;
  					}
  				}
  				
  				dataChecked = true;
  				
  				if (! files.length) {
  					dfrd.reject(['errUploadNoFiles']);
  				}
  				
  				xhr.open('POST', self.uploadURL, true);
  				
  				// set request headers
  				if (fm.customHeaders) {
  					$.each(fm.customHeaders, function(key) {
  						xhr.setRequestHeader(key, this);
  					});
  				}
  				
  				// set xhrFields
  				if (fm.xhrFields) {
  					$.each(fm.xhrFields, function(key) {
  						if (key in xhr) {
  							xhr[key] = this;
  						}
  					});
  				}
  
  				formData.append('cmd', 'upload');
  				formData.append(self.newAPI ? 'target' : 'current', target);
  				if (renames && renames.length) {
  					$.each(renames, function(i, v) {
  						formData.append('renames[]', v);
  					});
  					formData.append('suffix', fm.options.backupSuffix);
  				}
  				if (hashes) {
  					$.each(hashes, function(i, v) {
  						formData.append('hashes['+ i +']', v);
  					});
  				}
  				$.each(self.options.customData, function(key, val) {
  					formData.append(key, val);
  				});
  				$.each(self.options.onlyMimes, function(i, mime) {
  					formData.append('mimes['+i+']', mime);
  				});
  				
  				$.each(files, function(i, file) {
  					if (file._chunkmerged) {
  						formData.append('chunk', file._chunkmerged);
  						formData.append('upload[]', file._name);
  						formData.append('mtime[]', file._mtime);
  					} else {
  						if (file._chunkfail) {
  							formData.append('upload[]', 'chunkfail');
  							formData.append('mimes', 'chunkfail');
  						} else {
  							formData.append('upload[]', file);
  						}
  						if (file._chunk) {
  							formData.append('chunk', file._chunk);
  							formData.append('cid'  , file._cid);
  							formData.append('range', file._range);
  						}
  						formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
  					}
  					if (fm.UA.iOS) {
  						formData.append('overwrite', 0);
  					}
  				});
  				
  				if (isDataType) {
  					$.each(paths, function(i, path) {
  						formData.append('upload_path[]', path);
  					});
  				}
  				
  				
  				xhr.onreadystatechange = function() {
  					if (xhr.readyState == 4 && xhr.status == 0) {
  						if (abort) {
  							dfrd.reject();
  						} else {
  							var errors = ['errAbort'];
  							// ff bug while send zero sized file
  							// for safari - send directory
  							if (!isDataType && data.files && $.map(data.files, function(f){return f.size === 0? f : null;}).length) {
  								errors.push('errFolderUpload');
  							}
  							dfrd.reject(errors);
  						}
  					}
  				};
  				
  				xhr.send(formData);
  				
  				return true;
  			};
  			
  			if (! isDataType) {
  				if (files.length > 0) {
  					if (renames == null) {
  						renames = [];
  						hashes = {};
  						self.uploads.checkExists(files, target, fm).done(
  							function(res, res2){
  								if (fm.option('uploadOverwrite') && ! fm.UA.iOS) {
  									renames = res;
  									hashes = res2;
  									files = $.map(files, function(file){return !file._remove? file : null ;});
  								}
  								cnt = files.length;
  								if (cnt > 0) {
  									if (! send(files)) {
  										dfrd.reject();
  									}
  								} else {
  									dfrd.reject();
  								}
  							}
  						);
  					} else {
  						if (! send(files)) {
  							dfrd.reject();
  						}
  					}
  				} else {
  					dfrd.reject();
  				}
  			} else {
  				if (dataChecked) {
  					send(files[0], files[1]);
  				} else {
  					files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
  						renames = [];
  						cnt = result[0].length;
  						if (cnt) {
  							if (result[4] && result[4].length) {
  								// ensure directories
  								fm.request({
  									data   : {cmd : 'mkdir', target : target, dirs : result[4]},
  									notify : {type : 'mkdir', cnt : result[4].length}
  								})
  								.fail(function(error) {
  									error = error || ['errUnknown'];
  									if (error[0] === 'errCmdParams') {
  										multiMax = 1;
  									} else {
  										multiMax = 0;
  										dfrd.reject(error);
  									}
  								})
  								.done(function(data) {
  									if (data.hashes) {
  										result[1] = $.map(result[1], function(p) {
  											p = p.replace(/\/[^\/]*$/, '');
  											if (p === '') {
  												return target;
  											} else {
  												return data.hashes[p];
  											}
  										});
  									}
  								})
  								.always(function(data) {
  									if (multiMax) {
  										renames = result[2];
  										hashes = result[3];
  										send(result[0], result[1]);
  									}
  								});
  								return;
  							} else {
  								result[1] = $.map(result[1], function() { return target; });
  							}
  							renames = result[2];
  							hashes = result[3];
  							send(result[0], result[1]);
  						} else {
  							dfrd.reject(['errUploadNoFiles']);
  						}
  					}).fail(function(){
  						dfrd.reject();
  					});
  				}
  			}
  
  			return dfrd;
  		},
  		
  		// upload transport using iframe
  		iframe : function(data, fm) { 
  			var self   = fm ? fm : this,
  				input  = data.input? data.input : false,
  				files  = !input ? self.uploads.checkFile(data, self) : false,
  				dfrd   = $.Deferred()
  					.fail(function(error) {
  						error && self.error(error);
  					})
  					.done(function(data) {
  						data.warning && self.error(data.warning);
  						data.removed && self.remove(data);
  						data.added   && self.add(data);
  						data.changed && self.change(data);
  						self.trigger('upload', data);
  						data.sync && self.sync();
  					}),
  				name = 'iframe-'+namespace+(++self.iframeCnt),
  				form = $('<form action="'+self.uploadURL+'" method="post" enctype="multipart/form-data" encoding="multipart/form-data" target="'+name+'" style="display:none"><input type="hidden" name="cmd" value="upload" /></form>'),
  				msie = this.UA.IE,
  				// clear timeouts, close notification dialog, remove form/iframe
  				onload = function() {
  					abortto  && clearTimeout(abortto);
  					notifyto && clearTimeout(notifyto);
  					notify   && self.notify({type : 'upload', cnt : -cnt});
  					
  					setTimeout(function() {
  						msie && $('<iframe src="javascript:false;"/>').appendTo(form);
  						form.remove();
  						iframe.remove();
  					}, 100);
  				},
  				iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />')
  					.on('load', function() {
  						iframe.off('load')
  							.on('load', function() {
  								//var data = self.parseUploadData(iframe.contents().text());
  								
  								onload();
  								dfrd.reject();
  								//data.error ? dfrd.reject(data.error) : dfrd.resolve(data);
  							});
  							
  							// notify dialog
  							notifyto = setTimeout(function() {
  								notify = true;
  								self.notify({type : 'upload', cnt : cnt});
  							}, self.options.notifyDelay);
  							
  							// emulate abort on timeout
  							if (self.options.iframeTimeout > 0) {
  								abortto = setTimeout(function() {
  									onload();
  									dfrd.reject([errors.connect, errors.timeout]);
  								}, self.options.iframeTimeout);
  							}
  							
  							form.submit();
  					}),
  				target  = (data.target || self.cwd().hash),
  				names   = [],
  				dfds    = [],
  				renames = [],
  				hashes  = {},
  				cnt, notify, notifyto, abortto;
  
  			if (files && files.length) {
  				$.each(files, function(i, val) {
  					form.append('<input type="hidden" name="upload[]" value="'+val+'"/>');
  				});
  				cnt = 1;
  			} else if (input && $(input).is(':file') && $(input).val()) {
  				if (fm.option('uploadOverwrite') && ! fm.UA.iOS) {
  					names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
  					//names = $.map(names, function(file){return file.name? { name: file.name } : null ;});
  					dfds.push(self.uploads.checkExists(names, target, self).done(
  						function(res, res2){
  							renames = res;
  							hashes = res2;
  							cnt = $.map(names, function(file){return !file._remove? file : null ;}).length;
  							if (cnt != names.length) {
  								cnt = 0;
  							}
  						}
  					));
  				}
  				cnt = input.files ? input.files.length : 1;
  				form.append(input);
  			} else {
  				return dfrd.reject();
  			}
  			
  			$.when.apply($, dfds).done(function() {
  				if (cnt < 1) {
  					return dfrd.reject();
  				}
  				form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>')
  					.append('<input type="hidden" name="html" value="1"/>')
  					.append('<input type="hidden" name="node" value="'+self.id+'"/>')
  					.append($(input).attr('name', 'upload[]'));
  				
  				if (renames.length > 0) {
  					$.each(renames, function(i, rename) {
  						form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>');
  					});
  					form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>');
  				}
  				if (hashes) {
  					$.each(renames, function(i, v) {
  						form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>');
  					});
  				}
  				
  				$.each(self.options.onlyMimes||[], function(i, mime) {
  					form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>');
  				});
  				
  				$.each(self.options.customData, function(key, val) {
  					form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>');
  				});
  				
  				form.appendTo('body');
  				iframe.appendTo('body');
  			});
  			
  			return dfrd;
  		}
  	},
  	
  	
  	/**
  	 * Bind callback to event(s) The callback is executed at most once per event.
  	 * To bind to multiply events at once, separate events names by space
  	 *
  	 * @param  String    event name
  	 * @param  Function  callback
  	 * @return elFinder
  	 */
  	one : function(event, callback) {
  		var self = this,
  			h    = function(e, f) {
  				setTimeout(function() {self.unbind(event, h);}, 3);
  				return callback.apply(self.getListeners(e.type), arguments);
  			};
  		return this.bind(event, h);
  	},
  	
  	/**
  	 * Set/get data into/from localStorage
  	 *
  	 * @param  String       key
  	 * @param  String|void  value
  	 * @return String
  	 */
  	localStorage : function(key, val) {
  		var s      = window.localStorage,
  			oldkey = 'elfinder-'+key+this.id, // old key of elFinder < 2.1.6
  			retval, oldval,t;
  
  		// new key of elFinder >= 2.1.6
  		key = window.location.pathname+'-elfinder-'+key+this.id;
  		
  		if (val === null) {
  			return s.removeItem(key);
  		}
  		
  		if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
  			val = oldval;
  			s.removeItem(oldkey);
  		}
  		
  		if (val !== void(0)) {
  			t = typeof val;
  			if (t !== 'string' && t !== 'number') {
  				val = JSON.stringify(val);
  			}
  			try {
  				s.setItem(key, val);
  			} catch (e) {
  				s.clear();
  				s.setItem(key, val);
  			}
  			retval = s.getItem(key);
  		}
  
  		if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  			try {
  				return JSON.parse(retval);
  			} catch(e) {}
  		}
  		return retval;
  	},
  	
  	/**
  	 * Get/set cookie
  	 *
  	 * @param  String       cookie name
  	 * @param  String|void  cookie value
  	 * @return String
  	 */
  	cookie : function(name, value) {
  		var d, o, c, i, retval, t;
  
  		name = 'elfinder-'+name+this.id;
  
  		if (value === void(0)) {
  			if (document.cookie && document.cookie != '') {
  				c = document.cookie.split(';');
  				name += '=';
  				for (i=0; i<c.length; i++) {
  					c[i] = $.trim(c[i]);
  					if (c[i].substring(0, name.length) == name) {
  						retval = decodeURIComponent(c[i].substring(name.length));
  						if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') {
  							try {
  								return JSON.parse(retval);
  							} catch(e) {}
  						}
  						return retval;
  					}
  				}
  			}
  			return '';
  		}
  
  		o = $.extend({}, this.options.cookie);
  		if (value === null) {
  			value = '';
  			o.expires = -1;
  		} else {
  			t = typeof value;
  			if (t !== 'string' && t !== 'number') {
  				value = JSON.stringify(value);
  			}
  		}
  		if (typeof(o.expires) == 'number') {
  			d = new Date();
  			d.setTime(d.getTime()+(o.expires * 86400000));
  			o.expires = d;
  		}
  		document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
  		return value;
  	},
  	
  	/**
  	 * Get start directory (by location.hash or last opened directory)
  	 * 
  	 * @return String
  	 */
  	startDir : function() {
  		var locHash = window.location.hash;
  		if (locHash && locHash.match(/^#elf_/)) {
  			return locHash.replace(/^#elf_/, '');
  		} else if (this.options.startPathHash) {
  			return this.options.startPathHash;
  		} else {
  			return this.lastDir();
  		}
  	},
  	
  	/**
  	 * Get/set last opened directory
  	 * 
  	 * @param  String|undefined  dir hash
  	 * @return String
  	 */
  	lastDir : function(hash) { 
  		return this.options.rememberLastDir ? this.storage('lastdir', hash) : '';
  	},
  	
  	/**
  	 * Node for escape html entities in texts
  	 * 
  	 * @type jQuery
  	 */
  	_node : $('<span/>'),
  	
  	/**
  	 * Replace not html-safe symbols to html entities
  	 * 
  	 * @param  String  text to escape
  	 * @return String
  	 */
  	escape : function(name) {
  		return this._node.text(name).html().replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  	},
  	
  	/**
  	 * Cleanup ajax data.
  	 * For old api convert data into new api format
  	 * 
  	 * @param  String  command name
  	 * @param  Object  data from backend
  	 * @return Object
  	 */
  	normalize : function(data) {
  		var filter = function(file) { 
  		
  			if (file && file.hash && file.name && file.mime) {
  				if (file.mime == 'application/x-empty') {
  					file.mime = 'text/plain';
  				}
  				return file;
  			}
  			return null;
  		};
  		
  
  		if (data.files) {
  			data.files = $.map(data.files, filter);
  		} 
  		if (data.tree) {
  			data.tree = $.map(data.tree, filter);
  		}
  		if (data.added) {
  			data.added = $.map(data.added, filter);
  		}
  		if (data.changed) {
  			data.changed = $.map(data.changed, filter);
  		}
  		if (data.api) {
  			data.init = true;
  		}
  		return data;
  	},
  	
  	/**
  	 * Update sort options
  	 *
  	 * @param {String} sort type
  	 * @param {String} sort order
  	 * @param {Boolean} show folder first
  	 */
  	setSort : function(type, order, stickFolders) {
  		this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
  		this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
  		this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
  		this.trigger('sortchange');
  	},
  	
  	_sortRules : {
  		name : function(file1, file2) {
  			return elFinder.prototype.naturalCompare(file1.name, file2.name);
  		},
  		size : function(file1, file2) { 
  			var size1 = parseInt(file1.size) || 0,
  				size2 = parseInt(file2.size) || 0;
  				
  			return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
  		},
  		kind : function(file1, file2) {
  			return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
  		},
  		date : function(file1, file2) { 
  			var date1 = file1.ts || file1.date,
  				date2 = file2.ts || file2.date;
  
  			return date1 === date2 ? 0 : date1 > date2 ? 1 : -1
  		},
  		perm : function(file1, file2) { 
  			var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
  				v1  = val(file1),
  				v2  = val(file2);
  			return v1 === v2 ? 0 : v1 > v2 ? 1 : -1
  		},
  		mode : function(file1, file2) { 
  			var v1 = file1.mode || (file1.perm || ''),
  				v2 = file2.mode || (file2.perm || '');
  			return elFinder.prototype.naturalCompare(v1, v2);
  		},
  		owner : function(file1, file2) { 
  			var v1 = file1.owner || '',
  				v2 = file2.owner || '';
  			return elFinder.prototype.naturalCompare(v1, v2);
  		},
  		group : function(file1, file2) { 
  			var v1 = file1.group || '',
  				v2 = file2.group || '';
  			return elFinder.prototype.naturalCompare(v1, v2);
  		}
  	},
  	
  	/**
  	 * Valid sort rule names
  	 * 
  	 * @type Array
  	 */
  	sorters : [],
  	
  	/**
  	 * Compare strings for natural sort
  	 *
  	 * @param  String
  	 * @param  String
  	 * @return Number
  	 */
  	naturalCompare : function(a, b) {
  		var self = elFinder.prototype.naturalCompare;
  		if (typeof self.loc == 'undefined') {
  			self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
  		}
  		if (typeof self.sort == 'undefined') {
  			if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
  				// Native support
  				if (window.Intl && window.Intl.Collator) {
  					self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
  				} else {
  					self.sort = function(a, b) {
  						return a.localeCompare(b, self.loc, {numeric: true});
  					};
  				}
  			} else {
  				/*
  				 * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
  				 */
  				/*
  				 * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
  				 */
  				/*
  				 * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
  				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
  				 * http://opensource.org/licenses/mit-license.php
  				 */
  				self.sort = function(a, b) {
  					var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  					sre = /(^[ ]*|[ ]*$)/g,
  					dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  					hre = /^0x[0-9a-f]+$/i,
  					ore = /^0/,
  					syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
  					i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s },
  					// convert all to strings strip whitespace
  					// first character is "_", it's smallest - (Naoki Sawada)
  					x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
  					y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
  					// chunk/tokenize
  					xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  					yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  					// numeric, hex or date detection
  					xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
  					yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
  					oFxNcL, oFyNcL,
  					locRes = 0;
  
  					// first try and sort Hex codes or Dates
  					if (yD) {
  						if ( xD < yD ) return -1;
  						else if ( xD > yD ) return 1;
  					}
  					// natural sorting through split numeric strings and default strings
  					for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  
  						// find floats not starting with '0', string or 0 if not defined (Clint Priest)
  						oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  						oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  
  						// handle numeric vs string comparison - number < string - (Kyle Adams)
  						// but symbol first < number - (Naoki Sawada)
  						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
  							if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
  								return 1;
  							} else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
  								return -1;
  							}
  						}
  
  						// use decimal number comparison if either value is string zero
  						if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
  						if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
  
  						// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  						if (typeof oFxNcL !== typeof oFyNcL) {
  							oFxNcL += '';
  							oFyNcL += '';
  						}
  
  						// use locale sensitive sort for strings when case insensitive
  						// note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
  						if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
  							locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
  							if (locRes !== 0) return locRes;
  						}
  
  						if (oFxNcL < oFyNcL) return -1;
  						if (oFxNcL > oFyNcL) return 1;
  					}
  					return 0;
  				};
  				self.sort.insensitive = true;
  			}
  		}
  		return self.sort(a, b);
  	},
  	
  	/**
  	 * Compare files based on elFinder.sort
  	 *
  	 * @param  Object  file
  	 * @param  Object  file
  	 * @return Number
  	 */
  	compare : function(file1, file2) {
  		var self  = this,
  			type  = self.sortType,
  			asc   = self.sortOrder == 'asc',
  			stick = self.sortStickFolders,
  			rules = self.sortRules,
  			sort  = rules[type],
  			d1    = file1.mime == 'directory',
  			d2    = file2.mime == 'directory',
  			res;
  			
  		if (stick) {
  			if (d1 && !d2) {
  				return -1;
  			} else if (!d1 && d2) {
  				return 1;
  			}
  		}
  		
  		res = asc ? sort(file1, file2) : sort(file2, file1);
  		
  		return type !== 'name' && res === 0
  			? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
  			: res;
  	},
  	
  	/**
  	 * Sort files based on config
  	 *
  	 * @param  Array  files
  	 * @return Array
  	 */
  	sortFiles : function(files) {
  		return files.sort(this.compare);
  	},
  	
  	/**
  	 * Open notification dialog 
  	 * and append/update message for required notification type.
  	 *
  	 * @param  Object  options
  	 * @example  
  	 * this.notify({
  	 *    type : 'copy',
  	 *    msg : 'Copy files', // not required for known types @see this.notifyType
  	 *    cnt : 3,
  	 *    hideCnt  : false,   // true for not show count
  	 *    progress : 10,      // progress bar percents (use cnt : 0 to update progress bar)
  	 *    cancel   : callback // callback function for cancel button
  	 * })
  	 * @return elFinder
  	 */
  	notify : function(opts) {
  		var type     = opts.type,
  			msg      = this.messages['ntf'+type] ? this.i18n('ntf'+type) : this.i18n('ntfsmth'),
  			ndialog  = this.ui.notify,
  			notify   = ndialog.children('.elfinder-notify-'+type),
  			button   = notify.children('div.elfinder-notify-cancel').children('button'),
  			ntpl     = '<div class="elfinder-notify elfinder-notify-{type}"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"/><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"/><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"/></div><div class="elfinder-notify-cancel"/></div>',
  			delta    = opts.cnt,
  			size     = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
  			progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
  			cancel   = opts.cancel,
  			clhover  = 'ui-state-hover',
  			close    = function() {
  				notify._esc && $(document).off('keydown', notify._esc);
  				notify.remove();
  				!ndialog.children().length && ndialog.elfinderdialog('close');
  			},
  			cnt, total, prc;
  
  		if (!type) {
  			return this;
  		}
  		
  		if (!notify.length) {
  			notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg))
  				.appendTo(ndialog)
  				.data('cnt', 0);
  
  			if (progress != null) {
  				notify.data({progress : 0, total : 0});
  			}
  
  			if (cancel) {
  				button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+this.i18n('btnCancel')+'</span></button>')
  					.hover(function(e) { 
  						$(this).toggleClass(clhover, e.type == 'mouseenter');
  					});
  				notify.children('div.elfinder-notify-cancel').append(button);
  			}
  		}
  
  		cnt = delta + parseInt(notify.data('cnt'));
  		
  		if (cnt > 0) {
  			if (cancel && button.length) {
  				if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
  					notify._esc = function(e) {
  						if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) {
  							return;
  						}
  						e.preventDefault();
  						e.stopPropagation();
  						close();
  						if (cancel.promise) {
  							if (cancel.xhr) {
  								cancel.xhr.quiet = true;
  								cancel.xhr.abort();
  							}
  							cancel.reject();
  						} else {
  							cancel(e);
  						}
  					};
  					button.on('click', function(e) {
  						notify._esc(e);
  					});
  					$(document).on('keydown.' + this.namespace, notify._esc);
  				}
  			}
  			
  			!opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
  			ndialog.is(':hidden') && ndialog.elfinderdialog('open');
  			notify.data('cnt', cnt);
  			
  			if ((progress != null)
  			&& (total = notify.data('total')) >= 0
  			&& (prc = notify.data('progress')) >= 0) {
  
  				total += size != null? size : delta;
  				prc   += progress;
  				(size == null && delta < 0) && (prc += delta * 100);
  				notify.data({progress : prc, total : total});
  				if (size != null) {
  					prc *= 100;
  					total = Math.max(1, total);
  				}
  				progress = parseInt(prc/total);
  				
  				notify.find('.elfinder-notify-progress')
  					.animate({
  						width : (progress < 100 ? progress : 100)+'%'
  					}, 20);
  			}
  			
  		} else {
  			close();
  		}
  		
  		return this;
  	},
  	
  	/**
  	 * Open confirmation dialog 
  	 *
  	 * @param  Object  options
  	 * @example  
  	 * this.confirm({
  	 *    title : 'Remove files',
  	 *    text  : 'Here is question text',
  	 *    accept : {  // accept callback - required
  	 *      label : 'Continue',
  	 *      callback : function(applyToAll) { fm.log('Ok') }
  	 *    },
  	 *    cancel : { // cancel callback - required
  	 *      label : 'Cancel',
  	 *      callback : function() { fm.log('Cancel')}
  	 *    },
  	 *    reject : { // reject callback - optionally
  	 *      label : 'No',
  	 *      callback : function(applyToAll) { fm.log('No')}
  	 *    },
  	 *    buttons : [ // additional buttons callback - optionally
  	 *      {
  	 *        label : 'Btn1',
  	 *        callback : function(applyToAll) { fm.log('Btn1')}
  	 *      }
  	 *    ],
  	 *    all : true  // display checkbox "Apply to all"
  	 * })
  	 * @return elFinder
  	 */
  	confirm : function(opts) {
  		var self     = this,
  			complete = false,
  			options = {
  				cssClass  : 'elfinder-dialog-confirm',
  				modal     : true,
  				resizable : false,
  				title     : this.i18n(opts.title || 'confirmReq'),
  				buttons   : {},
  				close     : function() { 
  					!complete && opts.cancel.callback();
  					$(this).elfinderdialog('destroy');
  				}
  			},
  			apply = this.i18n('apllyAll'),
  			label, checkbox;
  
  		
  		options.buttons[this.i18n(opts.accept.label)] = function() {
  			opts.accept.callback(!!(checkbox && checkbox.prop('checked')))
  			complete = true;
  			$(this).elfinderdialog('close');
  		};
  		
  		if (opts.reject) {
  			options.buttons[this.i18n(opts.reject.label)] = function() {
  				opts.reject.callback(!!(checkbox && checkbox.prop('checked')))
  				complete = true;
  				$(this).elfinderdialog('close');
  			};
  		}
  		
  		if (opts.buttons && opts.buttons.length > 0) {
  			$.each(opts.buttons, function(i, v){
  				options.buttons[self.i18n(v.label)] = function() {
  					v.callback(!!(checkbox && checkbox.prop('checked')))
  					complete = true;
  					$(this).elfinderdialog('close');
  				};
  			});
  		}
  		
  		options.buttons[this.i18n(opts.cancel.label)] = function() {
  			$(this).elfinderdialog('close');
  		};
  		
  		if (opts.all) {
  			options.create = function() {
  				var base = $('<div class="elfinder-dialog-confirm-applyall"/>');
  				checkbox = $('<input type="checkbox" />');
  				$(this).next().find('.ui-dialog-buttonset')
  					.prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox)));
  			}
  		}
  		
  		return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options);
  	},
  	
  	/**
  	 * Create unique file name in required dir
  	 * 
  	 * @param  String  file name
  	 * @param  String  parent dir hash
  	 * @param  String  glue
  	 * @return String
  	 */
  	uniqueName : function(prefix, phash, glue) {
  		var i = 0, ext = '', p, name;
  		
  		prefix = this.i18n(prefix); 
  		phash = phash || this.cwd().hash;
  		glue = (typeof glue === 'undefined')? ' ' : glue;
  
  		if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
  			ext    = p[2];
  			prefix = p[1];
  		}
  		
  		name   = prefix+ext;
  		
  		if (!this.fileByName(name, phash)) {
  			return name;
  		}
  		while (i < 10000) {
  			name = prefix + glue + (++i) + ext;
  			if (!this.fileByName(name, phash)) {
  				return name;
  			}
  		}
  		return prefix + Math.random() + ext;
  	},
  	
  	/**
  	 * Return message translated onto current language
  	 * Allowed accept HTML element that was wrapped in jQuery object
  	 * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
  	 *
  	 * @param  String|Array  message[s]|Object jQuery
  	 * @return String
  	 **/
  	i18n : function() {
  		var self = this,
  			messages = this.messages, 
  			input    = [],
  			ignore   = [], 
  			message = function(m) {
  				var file;
  				if (m.indexOf('#') === 0) {
  					if ((file = self.file(m.substr(1)))) {
  						return file.name;
  					}
  				}
  				return m;
  			},
  			i, j, m;
  			
  		for (i = 0; i< arguments.length; i++) {
  			m = arguments[i];
  			
  			if (typeof m == 'string') {
  				input.push(message(m));
  			} else if ($.isArray(m)) {
  				for (j = 0; j < m.length; j++) {
  					if (typeof m[j] == 'string') {
  						input.push(message(m[j]));
  					} else if (m[j] instanceof jQuery) {
  						// jQuery object is HTML element
  						input.push(m[j]);
  					}
  				}
  			} else if (m instanceof jQuery) {
  				// jQuery object is HTML element
  				input.push(m[j]);
  			}
  		}
  		
  		for (i = 0; i < input.length; i++) {
  			// dont translate placeholders
  			if ($.inArray(i, ignore) !== -1) {
  				continue;
  			}
  			m = input[i];
  			if (typeof m == 'string') {
  				// translate message
  				m = messages[m] || self.escape(m);
  				// replace placeholders in message
  				m = m.replace(/\$(\d+)/g, function(match, placeholder) {
  					placeholder = i + parseInt(placeholder);
  					if (placeholder > 0 && input[placeholder]) {
  						ignore.push(placeholder)
  					}
  					return self.escape(input[placeholder]) || '';
  				});
  			} else {
  				// get HTML from jQuery object
  				m = m.get(0).outerHTML;
  			}
  
  			input[i] = m;
  		}
  
  		return $.map(input, function(m, i) { return $.inArray(i, ignore) === -1 ? m : null; }).join('<br>');
  	},
  	
  	/**
  	 * Convert mimetype into css classes
  	 * 
  	 * @param  String  file mimetype
  	 * @return String
  	 */
  	mime2class : function(mime) {
  		var prefix = 'elfinder-cwd-icon-';
  		
  		mime = mime.split('/');
  		
  		return prefix+mime[0]+(mime[0] != 'image' && mime[1] ? ' '+prefix+mime[1].replace(/(\.|\+)/g, '-') : '');
  	},
  	
  	/**
  	 * Return localized kind of file
  	 * 
  	 * @param  Object|String  file or file mimetype
  	 * @return String
  	 */
  	mime2kind : function(f) {
  		var mime = typeof(f) == 'object' ? f.mime : f, kind;
  		
  		if (f.alias && f.mime != 'symlink-broken') {
  			kind = 'Alias';
  		} else if (this.kinds[mime]) {
  			kind = this.kinds[mime];
  		} else {
  			if (mime.indexOf('text') === 0) {
  				kind = 'Text';
  			} else if (mime.indexOf('image') === 0) {
  				kind = 'Image';
  			} else if (mime.indexOf('audio') === 0) {
  				kind = 'Audio';
  			} else if (mime.indexOf('video') === 0) {
  				kind = 'Video';
  			} else if (mime.indexOf('application') === 0) {
  				kind = 'App';
  			} else {
  				kind = mime;
  			}
  		}
  		
  		return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
  	},
  	
  	/**
  	 * Return localized date
  	 * 
  	 * @param  Object  file object
  	 * @return String
  	 */
  	formatDate : function(file, ts) {
  		var self = this, 
  			ts   = ts || file.ts, 
  			i18  = self.i18,
  			date, format, output, d, dw, m, y, h, g, i, s;
  
  		if (self.options.clientFormatDate && ts > 0) {
  
  			date = new Date(ts*1000);
  			
  			h  = date[self.getHours]();
  			g  = h > 12 ? h - 12 : h;
  			i  = date[self.getMinutes]();
  			s  = date[self.getSeconds]();
  			d  = date[self.getDate]();
  			dw = date[self.getDay]();
  			m  = date[self.getMonth]() + 1;
  			y  = date[self.getFullYear]();
  			
  			format = ts >= this.yesterday 
  				? this.fancyFormat 
  				: this.dateFormat;
  
  			output = format.replace(/[a-z]/gi, function(val) {
  				switch (val) {
  					case 'd': return d > 9 ? d : '0'+d;
  					case 'j': return d;
  					case 'D': return self.i18n(i18.daysShort[dw]);
  					case 'l': return self.i18n(i18.days[dw]);
  					case 'm': return m > 9 ? m : '0'+m;
  					case 'n': return m;
  					case 'M': return self.i18n(i18.monthsShort[m-1]);
  					case 'F': return self.i18n(i18.months[m-1]);
  					case 'Y': return y;
  					case 'y': return (''+y).substr(2);
  					case 'H': return h > 9 ? h : '0'+h;
  					case 'G': return h;
  					case 'g': return g;
  					case 'h': return g > 9 ? g : '0'+g;
  					case 'a': return h >= 12 ? 'pm' : 'am';
  					case 'A': return h >= 12 ? 'PM' : 'AM';
  					case 'i': return i > 9 ? i : '0'+i;
  					case 's': return s > 9 ? s : '0'+s;
  				}
  				return val;
  			});
  			
  			return ts >= this.yesterday
  				? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
  				: output;
  		} else if (file.date) {
  			return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
  		}
  		
  		return self.i18n('dateUnknown');
  	},
  	
  	/**
  	 * Return css class marks file permissions
  	 * 
  	 * @param  Object  file 
  	 * @return String
  	 */
  	perms2class : function(o) {
  		var c = '';
  		
  		if (!o.read && !o.write) {
  			c = 'elfinder-na';
  		} else if (!o.read) {
  			c = 'elfinder-wo';
  		} else if (!o.write) {
  			c = 'elfinder-ro';
  		}
  		return c;
  	},
  	
  	/**
  	 * Return localized string with file permissions
  	 * 
  	 * @param  Object  file
  	 * @return String
  	 */
  	formatPermissions : function(f) {
  		var p  = [];
  			
  		f.read && p.push(this.i18n('read'));
  		f.write && p.push(this.i18n('write'));	
  
  		return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
  	},
  	
  	/**
  	 * Return formated file size
  	 * 
  	 * @param  Number  file size
  	 * @return String
  	 */
  	formatSize : function(s) {
  		var n = 1, u = 'b';
  		
  		if (s == 'unknown') {
  			return this.i18n('unknown');
  		}
  		
  		if (s > 1073741824) {
  			n = 1073741824;
  			u = 'GB';
  		} else if (s > 1048576) {
  			n = 1048576;
  			u = 'MB';
  		} else if (s > 1024) {
  			n = 1024;
  			u = 'KB';
  		}
  		s = s/n;
  		return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
  	},
  	
  	/**
  	 * Return formated file mode by options.fileModeStyle
  	 * 
  	 * @param  String  file mode
  	 * @param  String  format style
  	 * @return String
  	 */
  	formatFileMode : function(p, style) {
  		var i, o, s, b, sticy, suid, sgid, str, oct;
  		
  		if (!style) {
  			style = this.options.fileModeStyle.toLowerCase();
  		}
  		p = $.trim(p);
  		if (p.match(/[rwxs-]{9}$/i)) {
  			str = p = p.substr(-9);
  			if (style == 'string') {
  				return str;;
  			}
  			oct = '';
  			s = 0;
  			for (i=0; i<7; i=i+3) {
  				o = p.substr(i, 3);
  				b = 0;
  				if (o.match(/[r]/i)) {
  					b += 4;
  				}
  				if (o.match(/[w]/i)) {
  					b += 2;
  				}
  				if (o.match(/[xs]/i)) {
  					if (o.match(/[xs]/)) {
  						b += 1;
  					}
  					if (o.match(/[s]/i)) {
  						if (i == 0) {
  							s += 4;
  						} else if (i == 3) {
  							s += 2;
  						}
  					}
  				}
  				oct += b.toString(8);
  			}
  			if (s) {
  				oct = s.toString(8) + oct;
  			}
  		} else {
  			p = parseInt(p, 8);
  			oct = p? p.toString(8) : '';
  			if (!p || style == 'octal') {
  				return oct;
  			}
  			o = p.toString(8);
  			s = 0;
  			if (o.length > 3) {
  				o = o.substr(-4);
  				s = parseInt(o.substr(0, 1), 8);
  				o = o.substr(1);
  			}
  			sticy = ((s & 1) == 1); // not support
  			sgid = ((s & 2) == 2);
  			suid = ((s & 4) == 4);
  			str = '';
  			for(i=0; i<3; i++) {
  				if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
  					str += 'r';
  				} else {
  					str += '-';
  				}
  				if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
  					str += 'w';
  				} else {
  					str += '-';
  				}
  				if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
  					str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
  				} else {
  					str += '-';
  				}
  			}
  		}
  		if (style == 'both') {
  			return str + ' (' + oct + ')';
  		} else if (style == 'string') {
  			return str;
  		} else {
  			return oct;
  		}
  	},
  	
  	navHash2Id : function(hash) {
  		return this.navPrefix + hash;
  	},
  	
  	navId2Hash : function(id) {
  		return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false;
  	},
  	
  	cwdHash2Id : function(hash) {
  		return this.cwdPrefix + hash;
  	},
  	
  	cwdId2Hash : function(id) {
  		return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false;
  	},
  	
  	isInWindow : function(elem, nochkHide) {
  		if (! nochkHide && elem.is(':hidden')) {
  			return false;
  		}
  		var rect = elem[0].getBoundingClientRect();
  		return document.elementFromPoint(rect.left, rect.top)? true : false;
  	},
  	
  	log : function(m) { window.console && window.console.log && window.console.log(m); return this; },
  	
  	debug : function(type, m) {
  		var d = this.options.debug;
  
  		if (d == 'all' || d === true || ($.isArray(d) && $.inArray(type, d) != -1)) {
  			window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m);
  		} 
  		return this;
  	},
  	time : function(l) { window.console && window.console.time && window.console.time(l); },
  	timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); }
  	
  
  }
  
  /**
   * for conpat ex. ie8...
   *
   * Object.keys() - JavaScript | MDN
   * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
   */
  if (!Object.keys) {
  	Object.keys = (function () {
  		var hasOwnProperty = Object.prototype.hasOwnProperty,
  				hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
  				dontEnums = [
  					'toString',
  					'toLocaleString',
  					'valueOf',
  					'hasOwnProperty',
  					'isPrototypeOf',
  					'propertyIsEnumerable',
  					'constructor'
  				],
  				dontEnumsLength = dontEnums.length
  
  		return function (obj) {
  			if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object')
  
  			var result = []
  
  			for (var prop in obj) {
  				if (hasOwnProperty.call(obj, prop)) result.push(prop)
  			}
  
  			if (hasDontEnumBug) {
  				for (var i=0; i < dontEnumsLength; i++) {
  					if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i])
  				}
  			}
  			return result
  		}
  	})()
  };
  
  
  /*
   * File: /js/elFinder.version.js
   */
  
  /**
   * Application version
   *
   * @type String
   **/
  elFinder.prototype.version = '2.1.13';
  
  
  
  /*
   * File: /js/jquery.elfinder.js
   */
  
  /*** jQuery UI droppable performance tune for elFinder ***/
  (function(){
  if ($.ui && $.ui.ddmanager) {
  	var origin = $.ui.ddmanager.prepareOffsets;
  	$.ui.ddmanager.prepareOffsets = function( t, event ) {
  		var isOutView = function(elem) {
  			if (elem.is(':hidden')) {
  				return true;
  			}
  			var rect = elem[0].getBoundingClientRect();
  			return document.elementFromPoint(rect.left, rect.top)? false : true;
  		}
  		
  		if (event.type === 'mousedown' || t.options.elfRefresh) {
  			var i, d,
  			m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  			l = m.length;
  			for ( i = 0; i < l; i++ ) {
  				d = m[ i ];
  				if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) {
  					d.options.disabled = isOutView(d.element);
  					d.options.autoDisable = d.options.disabled? 2 : 1;
  				}
  			}
  		}
  		
  		// call origin function
  		return origin( t, event );
  	};
  }
  })();
  
  /*!
   * jQuery UI Touch Punch 0.2.3
   *
   * Copyright 2011–2014, Dave Furfero
   * Dual licensed under the MIT or GPL Version 2 licenses.
   *
   * Depends:
   *	jquery.ui.widget.js
   *	jquery.ui.mouse.js
   */
  (function ($) {
  
    // Detect touch support
    $.support.touch = 'ontouchend' in document;
  
    // Ignore browsers without touch support
    if (!$.support.touch) {
  	return;
    }
  
    var mouseProto = $.ui.mouse.prototype,
  	  _mouseInit = mouseProto._mouseInit,
  	  _mouseDestroy = mouseProto._mouseDestroy,
  	  touchHandled,
  	  posX, posY;
  
    /**
     * Simulate a mouse event based on a corresponding touch event
     * @param {Object} event A touch event
     * @param {String} simulatedType The corresponding mouse event
     */
    function simulateMouseEvent (event, simulatedType) {
  
  	// Ignore multi-touch events
  	if (event.originalEvent.touches.length > 1) {
  	  return;
  	}
  
  	if (! $(event.currentTarget).hasClass('touch-punch-keep-default')) {
  		event.preventDefault();
  	}
  
  	var touch = event.originalEvent.changedTouches[0],
  		simulatedEvent = document.createEvent('MouseEvents');
  	
  	// Initialize the simulated mouse event using the touch event's coordinates
  	simulatedEvent.initMouseEvent(
  	  simulatedType,	// type
  	  true,				// bubbles					  
  	  true,				// cancelable				  
  	  window,			// view						  
  	  1,				// detail					  
  	  touch.screenX,	// screenX					  
  	  touch.screenY,	// screenY					  
  	  touch.clientX,	// clientX					  
  	  touch.clientY,	// clientY					  
  	  false,			// ctrlKey					  
  	  false,			// altKey					  
  	  false,			// shiftKey					  
  	  false,			// metaKey					  
  	  0,				// button					  
  	  null				// relatedTarget			  
  	);
  
  	// Dispatch the simulated event to the target element
  	event.target.dispatchEvent(simulatedEvent);
    }
  
    /**
     * Handle the jQuery UI widget's touchstart events
     * @param {Object} event The widget element's touchstart event
     */
    mouseProto._touchStart = function (event) {
  
  	var self = this;
  
  	// Ignore the event if another widget is already being handled
  	if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
  	  return;
  	}
  
  	// Track element position to avoid "false" move
  	posX = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  	posY = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  
  	// Set the flag to prevent other widgets from inheriting the touch event
  	touchHandled = true;
  
  	// Track movement to determine if interaction was a click
  	self._touchMoved = false;
  
  	// Simulate the mouseover event
  	simulateMouseEvent(event, 'mouseover');
  
  	// Simulate the mousemove event
  	simulateMouseEvent(event, 'mousemove');
  
  	// Simulate the mousedown event
  	simulateMouseEvent(event, 'mousedown');
    };
  
    /**
     * Handle the jQuery UI widget's touchmove events
     * @param {Object} event The document's touchmove event
     */
    mouseProto._touchMove = function (event) {
  
  	// Ignore event if not handled
  	if (!touchHandled) {
  	  return;
  	}
  
  	// Ignore if it's a "false" move (position not changed)
  	var x = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  	var y = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  	// Ignore if it's a "false" move (position not changed)
  	if (Math.abs(posX - x) <= 2 && Math.abs(posY - y) <= 2) {
  		return;
  	}
  
  	// Interaction was not a click
  	this._touchMoved = true;
  
  	// Simulate the mousemove event
  	simulateMouseEvent(event, 'mousemove');
    };
  
    /**
     * Handle the jQuery UI widget's touchend events
     * @param {Object} event The document's touchend event
     */
    mouseProto._touchEnd = function (event) {
  
  	// Ignore event if not handled
  	if (!touchHandled) {
  	  return;
  	}
  
  	// Simulate the mouseup event
  	simulateMouseEvent(event, 'mouseup');
  
  	// Simulate the mouseout event
  	simulateMouseEvent(event, 'mouseout');
  
  	// If the touch interaction did not move, it should trigger a click
  	if (!this._touchMoved) {
  
  	  // Simulate the click event
  	  simulateMouseEvent(event, 'click');
  	}
  
  	// Unset the flag to allow other widgets to inherit the touch event
  	touchHandled = false;
  	this._touchMoved = false;
    };
  
    /**
     * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
     * This method extends the widget with bound touch event handlers that
     * translate touch events to mouse events and pass them to the widget's
     * original mouse event handling methods.
     */
    mouseProto._mouseInit = function () {
  	
  	var self = this;
  
  	if (self.element.hasClass('touch-punch')) {
  		// Delegate the touch handlers to the widget's element
  		self.element.bind({
  		  touchstart: $.proxy(self, '_touchStart'),
  		  touchmove: $.proxy(self, '_touchMove'),
  		  touchend: $.proxy(self, '_touchEnd')
  		});
  	}
  
  	// Call the original $.ui.mouse init method
  	_mouseInit.call(self);
    };
  
    /**
     * Remove the touch event handlers
     */
    mouseProto._mouseDestroy = function () {
  	
  	var self = this;
  
  	if (self.element.hasClass('touch-punch')) {
  		// Delegate the touch handlers to the widget's element
  		self.element.unbind({
  		  touchstart: $.proxy(self, '_touchStart'),
  		  touchmove: $.proxy(self, '_touchMove'),
  		  touchend: $.proxy(self, '_touchEnd')
  		});
  	}
  
  	// Call the original $.ui.mouse destroy method
  	_mouseDestroy.call(self);
    };
  
  })(jQuery);
  
  $.fn.elfinder = function(o) {
  	
  	if (o == 'instance') {
  		return this.getElFinder();
  	}
  	
  	return this.each(function() {
  		
  		var cmd = typeof(o) == 'string' ? o : '';
  		if (!this.elfinder) {
  			new elFinder(this, typeof(o) == 'object' ? o : {})
  		}
  		
  		switch(cmd) {
  			case 'close':
  			case 'hide':
  				this.elfinder.hide();
  				break;
  				
  			case 'open':
  			case 'show':
  				this.elfinder.show();
  				break;
  				
  			case'destroy':
  				this.elfinder.destroy();
  				break;
  		}
  		
  	})
  };
  
  $.fn.getElFinder = function() {
  	var instance;
  	
  	this.each(function() {
  		if (this.elfinder) {
  			instance = this.elfinder;
  			return false;
  		}
  	});
  	
  	return instance;
  };
  
  $.fn.elfUiWidgetInstance = function(name) {
  	try {
  		return this[name]('instance');
  	} catch(e) {
  		// fallback for jQuery UI < 1.11
  		var data = this.data('ui-' + name);
  		if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) {
  			return data;
  		}
  		return null;
  	}
  }
  
  
  /*
   * File: /js/elFinder.options.js
   */
  
  /**
   * Default elFinder config
   *
   * @type  Object
   * @autor Dmitry (dio) Levashov
   */
  elFinder.prototype._options = {
  	/**
  	 * Connector url. Required!
  	 *
  	 * @type String
  	 */
  	url : '',
  
  	/**
  	 * Ajax request type.
  	 *
  	 * @type String
  	 * @default "get"
  	 */
  	requestType : 'get',
  
  	/**
  	 * Transport to send request to backend.
  	 * Required for future extensions using websockets/webdav etc.
  	 * Must be an object with "send" method.
  	 * transport.send must return $.Deferred() object
  	 *
  	 * @type Object
  	 * @default null
  	 * @example
  	 *  transport : {
  	 *    init : function(elfinderInstance) { },
  	 *    send : function(options) {
  	 *      var dfrd = $.Deferred();
  	 *      // connect to backend ...
  	 *      return dfrd;
  	 *    },
  	 *    upload : function(data) {
  	 *      var dfrd = $.Deferred();
  	 *      // upload ...
  	 *      return dfrd;
  	 *    }
  	 *    
  	 *  }
  	 **/
  	transport : {},
  
  	/**
  	 * URL to upload file to.
  	 * If not set - connector URL will be used
  	 *
  	 * @type String
  	 * @default  ''
  	 */
  	urlUpload : '',
  
  	/**
  	 * Allow to drag and drop to upload files
  	 *
  	 * @type Boolean|String
  	 * @default  'auto'
  	 */
  	dragUploadAllow : 'auto',
  	
  	/**
  	 * Max size of chunked data of file upload
  	 * 
  	 * @type Number
  	 * @default  10485760(10MB)
  	 */
  	uploadMaxChunkSize : 10485760,
  	
  	/**
  	 * Timeout for upload using iframe
  	 *
  	 * @type Number
  	 * @default  0 - no timeout
  	 */
  	iframeTimeout : 0,
  	
  	/**
  	 * Data to append to all requests and to upload files
  	 *
  	 * @type Object
  	 * @default  {}
  	 */
  	customData : {},
  	
  	/**
  	 * Event listeners to bind on elFinder init
  	 *
  	 * @type Object
  	 * @default  {}
  	 */
  	handlers : {},
  
  	/**
  	 * Any custom headers to send across every ajax request
  	 *
  	 * @type Object
  	 * @default {}
  	 */
  	customHeaders : {},
  
  	/**
  	 * Any custom xhrFields to send across every ajax request
  	 *
  	 * @type Object
  	 * @default {}
  	 */
  	xhrFields : {},
  
  	/**
  	 * Interface language
  	 *
  	 * @type String
  	 * @default "en"
  	 */
  	lang : 'en',
  
  	/**
  	 * Additional css class for filemanager node.
  	 *
  	 * @type String
  	 */
  	cssClass : '',
  
  	/**
  	 * Active commands list
  	 * If some required commands will be missed here, elFinder will add its
  	 *
  	 * @type Array
  	 */
  	commands : [
  		'open', 'opendir', 'reload', 'home', 'up', 'back', 'forward', 'getfile', 'quicklook', 
  		'download', 'rm', 'duplicate', 'rename', 'mkdir', 'mkfile', 'upload', 'copy', 
  		'cut', 'paste', 'edit', 'extract', 'archive', 'search', 'info', 'view', 'help',
  		'resize', 'sort', 'netmount', 'netunmount', 'places', 'chmod', 'colwidth'
  	],
  	
  	/**
  	 * Commands options.
  	 *
  	 * @type Object
  	 **/
  	commandsOptions : {
  		// // configure shortcuts of any command
  		// // add `shortcuts` property into each command
  		// any_command_name : {
  		// 	shortcuts : [] // for disable this command's shortcuts
  		// },
  		// any_command_name : {
  		// 	shortcuts : function(fm, shortcuts) {
  		// 		// for add `CTRL + E` for this command action
  		// 		shortcuts[0]['pattern'] += ' ctrl+e';
  		// 		return shortcuts;
  		// 	}
  		// },
  		// any_command_name : {
  		// 	shortcuts : function(fm, shortcuts) {
  		// 		// for full customize of this command's shortcuts
  		// 		return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ];
  		// 	}
  		// },
  		// "getfile" command options.
  		getfile : {
  			onlyURL  : false,
  			// allow to return multiple files info
  			multiple : false,
  			// allow to return filers info
  			folders  : false,
  			// action after callback (""/"close"/"destroy")
  			oncomplete : '',
  			// get image sizes before callback call
  			getImgSize : false
  		},
  		// "upload" command options.
  		upload : {
  			// Open system OS upload dialog: 'button' OR Open elFinder upload dialog: 'uploadbutton'
  			ui : 'button'
  		},
  		// "download" command options.
  		download : {
  			maxRequests : 10
  		},
  		// "quicklook" command options.
  		quicklook : {
  			autoplay : true,
  			jplayer  : 'extensions/jplayer',
  			// MIME types to use Google Docs online viewer
  			// Example ['application/pdf', 'image/tiff', 'application/vnd.ms-office', 'application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
  			googleDocsMimes : []
  		},
  		// "quicklook" command options.
  		edit : {
  			// list of allowed mimetypes to edit
  			// if empty - any text files can be edited
  			mimes : [],
  			// edit files in wysisyg's
  			editors : [
  				// {
  				// 	/**
  				// 	 * files mimetypes allowed to edit in current wysisyg
  				// 	 * @type  Array
  				// 	 */
  				// 	mimes : ['text/html'], 
  				// 	/**
  				// 	 * Called when "edit" dialog loaded.
  				// 	 * Place to init wysisyg.
  				// 	 * Can return wysisyg instance
  				// 	 *
  				// 	 * @param  DOMElement  textarea node
  				// 	 * @return Object
  				// 	 */
  				// 	load : function(textarea) { },
  				// 	/**
  				// 	 * Called before "edit" dialog closed.
  				// 	 * Place to destroy wysisyg instance.
  				// 	 *
  				// 	 * @param  DOMElement  textarea node
  				// 	 * @param  Object      wysisyg instance (if was returned by "load" callback)
  				// 	 * @return void
  				// 	 */
  				// 	close : function(textarea, instance) { },
  				// 	/**
  				// 	 * Called before file content send to backend.
  				// 	 * Place to update textarea content if needed.
  				// 	 *
  				// 	 * @param  DOMElement  textarea node
  				// 	 * @param  Object      wysisyg instance (if was returned by "load" callback)
  				// 	 * @return void
  				// 	 */
  				// 	save : function(textarea, instance) {},
  				// 	/**
  				// 	 * Called after load() or save().
  				// 	 * Set focus to wysisyg editor.
  				// 	 *
  				// 	 * @param  DOMElement  textarea node
  				// 	 * @param  Object      wysisyg instance (if was returned by "load" callback)
  				// 	 * @return void
  				// 	 */
  				// 	focus : function(textarea, instance) {}
  				// 
  				// }
  			]
  		},
  		search : {
  			// Incremental search from the current view
  			incsearch : {
  				enable : true, // is enable true or false
  				minlen : 1,    // minimum number of characters
  				wait   : 500   // wait milliseconds
  			}
  		},
  		// "info" command options.
  		info : {
  			nullUrlDirLinkSelf : true,
  			custom : {
  				// /**
  				//  * Example of custom info `desc`
  				//  */
  				// desc : {
  				// 	/**
  				// 	 * Lable (require)
  				// 	 * It is filtered by the `fm.i18n()`
  				// 	 * 
  				// 	 * @type String
  				// 	 */
  				// 	label : 'Description',
  				// 	
  				// 	/**
  				// 	 * Template (require)
  				// 	 * `{id}` is replaced in dialog.id
  				// 	 * 
  				// 	 * @type String
  				// 	 */
  				// 	tpl : '<div class="elfinder-info-desc"><span class="elfinder-info-spinner"></span></div>',
  				// 	
  				// 	/**
  				// 	 * Restricts to mimetypes (optional)
  				// 	 * Exact match or category match
  				// 	 * 
  				// 	 * @type Array
  				// 	 */
  				// 	mimes : ['text', 'image/jpeg', 'directory'],
  				// 	
  				// 	/**
  				// 	 * Restricts to file.hash (optional)
  				// 	 * 
  				// 	 * @ type Regex
  				// 	 */
  				// 	hashRegex : /^l\d+_/,
  				// 
  				// 	/**
  				// 	 * Request that asks for the description and sets the field (optional)
  				// 	 * 
  				// 	 * @type Function
  				// 	 */
  				// 	action : function(file, fm, dialog) {
  				// 		fm.request({
  				// 		data : { cmd : 'desc', target: file.hash },
  				// 			preventDefault: true,
  				// 		})
  				// 		.fail(function() {
  				// 			dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown'));
  				// 		})
  				// 		.done(function(data) {
  				// 			dialog.find('div.elfinder-info-desc').html(data.desc);
  				// 		});
  				// 	}
  				// }
  			}
  		},
  		mkdir: {
  			// Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton
  			intoNewFolderToolbtn: false,
  		},
  		netmount: {
  			ftp: {
  				name : 'FTP',
  				inputs: {
  					host     : $('<input type="text"/>'),
  					port     : $('<input type="text" placeholder="21"/>'),
  					path     : $('<input type="text" value="/"/>'),
  					user     : $('<input type="text"/>'),
  					pass     : $('<input type="password"/>'),
  					encoding : $('<input type="text" placeholder="Optional"/>'),
  					locale   : $('<input type="text" placeholder="Optional"/>')
  				}
  			},
  			dropbox: {
  				name : 'Dropbox.com',
  				inputs: {
  					host     : $('<span><span class="elfinder-info-spinner"/></span></span><input type="hidden"/>'),
  					path     : $('<input type="text" value="/"/>'),
  					user     : $('<input type="hidden"/>'),
  					pass     : $('<input type="hidden"/>')
  				},
  				select: function(fm){
  					var self = this;
  					if (self.inputs.host.find('span').length) {
  						fm.request({
  							data : {cmd : 'netmount', protocol: 'dropbox', host: 'dropbox.com', user: 'init', pass: 'init', options: {url: fm.uploadURL, id: fm.id}},
  							preventDefault : true
  						}).done(function(data){
  							self.inputs.host.find('span').removeClass("elfinder-info-spinner");
  							self.inputs.host.find('span').html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1,'Dropbox.com');}));
  						}).fail(function(){});
  					}					
  				},
  				done: function(fm, data){
  					var self = this;
  					if (data.mode == 'makebtn') {
  						self.inputs.host.find('span').removeClass("elfinder-info-spinner");
  						self.inputs.host.find('input').hover(function(){$(this).toggleClass("ui-state-hover");});
  						self.inputs.host[1].value = "";
  					} else {
  						self.inputs.host.find('span').removeClass("elfinder-info-spinner");
  						self.inputs.host.find('span').html("Dropbox.com");
  						self.inputs.host[1].value = "dropbox";
  						self.inputs.user.val("done");
  						self.inputs.pass.val("done");
  					}
  				}
  			},
  			googledrive: {
  				name : 'GoogleDrive',
  				inputs: {
  					offline  : $('<input type="checkbox"/>').on('change', function() {
  						$(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  					}),
  					host     : $('<span><span class="elfinder-info-spinner"/></span><input type="hidden"/>'),
  					path     : $('<input type="text" value="root"/>'),
  					user     : $('<input type="hidden"/>'),
  					pass     : $('<input type="hidden"/>')
  				},
  				select: function(fm, ev, data){
  					var f = this.inputs, oline = f.offline,
  						data = data || null;
  					if ($(f.host[0]).find('span.elfinder-info-spinner').length || data === 'reset') {
  						if (oline.parent().children().length === 1) {
  							f.path.parent().prev().html(fm.i18n('folderId'));
  							oline.attr('title', fm.i18n('offlineAccess'));
  							oline.uniqueId().after($('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  						}
  						$(f.host[0]).empty().addClass('elfinder-info-spinner')
  							.parent().find('span.elfinder-button-icon').remove();
  						fm.request({
  							data : {cmd : 'netmount', protocol: 'googledrive', host: 'google.com', user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  							preventDefault : true
  						}).done(function(data){
  							$(f.host[0]).removeClass("elfinder-info-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1,'Google.com');}));
  						}).fail(function(){});
  					} else {
  						oline.parent().parent()[f.user.val()? 'hide':'show']();
  					}
  				},
  				done: function(fm, data){
  					var f = this.inputs, p = this.protocol;
  					if (data.mode == 'makebtn') {
  						$(f.host[0]).removeClass('elfinder-info-spinner');
  						f.host.find('input').hover(function(){$(this).toggleClass('ui-state-hover');});
  						$(f.host[1]).val('');
  						f.path.val('root').next().remove();
  						f.user.val('');
  						f.pass.val('');
  						f.offline.parent().parent().show();
  					} else {
  						$(f.host[0]).html('Google.com&nbsp;').removeClass('elfinder-info-spinner');
  						if (data.reset) {
  							p.trigger('change', 'reset');
  							return;
  						}
  						$(f.host[0]).parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  							.on('click', function() {
  								$(f.host[1]).val('reauth');
  								p.trigger('change', 'reset');
  							}));
  						$(f.host[1]).val('googledrive');
  						if (data.folders) {
  							f.path.next().remove().end().after(
  								$('<div/>').append(
  									$('<select class="ui-corner-all" style="max-width:200px;">').append(
  										$($.map(data.folders, function(n,i){return '<option value="'+i+'">'+fm.escape(n)+'</option>'}).join(''))
  									).on('change', function(){f.path.val($(this).val());})
  								)
  							);
  						}
  						f.user.val('done');
  						f.pass.val('done');
  						f.offline.parent().parent().hide();
  					}
  				},
  				fail: function(fm, err){
  					this.protocol.trigger('change', 'reset');
  				}
  			}
  		},
  
  		help : {view : ['about', 'shortcuts', 'help', 'debug']}
  	},
  	
  	/**
  	 * Callback for "getfile" commands.
  	 * Required to use elFinder with WYSIWYG editors etc..
  	 *
  	 * @type Function
  	 * @default null (command not active)
  	 */
  	getFileCallback : null,
  	
  	/**
  	 * Default directory view. icons/list
  	 *
  	 * @type String
  	 * @default "icons"
  	 */
  	defaultView : 'icons',
  	
  	/**
  	 * Hash of default directory path to open
  	 * 
  	 * @type String
  	 * @default ""
  	 */
  	startPathHash : '',
  	
  	/**
  	 * UI plugins to load.
  	 * Current dir ui and dialogs loads always.
  	 * Here set not required plugins as folders tree/toolbar/statusbar etc.
  	 *
  	 * @type Array
  	 * @default ['toolbar', 'tree', 'path', 'stat']
  	 * @full ['toolbar', 'places', 'tree', 'path', 'stat']
  	 */
  	ui : ['toolbar', 'tree', 'path', 'stat'],
  	
  	/**
  	 * Some UI plugins options.
  	 * @type Object
  	 */
  	uiOptions : {
  		// toolbar configuration
  		toolbar : [
  			['back', 'forward'],
  			['netmount'],
  			// ['reload'],
  			// ['home', 'up'],
  			['mkdir', 'mkfile', 'upload'],
  			['open', 'download', 'getfile'],
  			['info', 'chmod'],
  			['quicklook'],
  			['copy', 'cut', 'paste'],
  			['rm'],
  			['duplicate', 'rename', 'edit', 'resize'],
  			['extract', 'archive'],
  			['search'],
  			['view', 'sort'],
  			['help'],
  			// extra options
  			{
  				// auto hide on initial open
  				autoHideUA: ['Mobile']
  			}
  		],
  		// directories tree options
  		tree : {
  			// expand current root on init
  			openRootOnLoad : true,
  			// expand current work directory on open
  			openCwdOnOpen  : true,
  			// auto load current dir parents
  			syncTree : true
  			// ,
  			// /**
  			//  * Add CSS class name to navbar directories (optional)
  			//  * see: https://github.com/Studio-42/elFinder/pull/1061,
  			//  *      https://github.com/Studio-42/elFinder/issues/1231
  			//  * 
  			//  * @type Function
  			//  */
  			// getClass: function(dir) {
  			// 	// e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  			// 	return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase();
  			// }
  		},
  		// navbar options
  		navbar : {
  			minWidth : 150,
  			maxWidth : 500,
  			// auto hide on initial open
  			autoHideUA: [] // e.g. ['Mobile']
  		},
  		cwd : {
  			// display parent folder with ".." name :)
  			oldSchool : false,
  			
  			// fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch']
  			showSelectCheckboxUA : ['Touch'],
  			
  			// file info columns displayed
  			listView : {
  				// name is always displayed, cols are ordered
  				// e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode']
  				// mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755))
  				// 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true`
  				columns : ['perm', 'date', 'size', 'kind'],
  				// override this if you want custom columns name
  				// example
  				// columnsCustomName : {
  				//		date : 'Last modification',
  				// 		kind : 'Mime type'
  				// }
  				columnsCustomName : {},
  				// fixed list header colmun
  				fixedHeader : true
  			}
  
  			// /**
  			//  * Add CSS class name to cwd directories (optional)
  			//  * see: https://github.com/Studio-42/elFinder/pull/1061,
  			//  *      https://github.com/Studio-42/elFinder/issues/1231
  			//  * 
  			//  * @type Function
  			//  */
  			// ,
  			// getClass: function(file) {
  			// 	// e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  			// 	return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase();
  			//}
  			
  			//,
  			//// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement
  			//replacement : {
  			//	tooltip : function(f, fm) {
  			//		var list = fm.viewType == 'list', // current view type
  			//			query = fm.searchStatus.state == 2, // is in search results
  			//			title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  			//			info  = '';
  			//		if (query && f.path) {
  			//			info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  			//		} else {
  			//			info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  			//		}
  			//		if (list) {
  			//			info += (info? '&#13;' : '') + fm.escape(f.name);
  			//		}
  			//		return info? info + '&#13;' + title : title;
  			//	}
  			//}
  		}
  	},
  
  	/**
  	 * Display only required files by types
  	 *
  	 * @type Array
  	 * @default []
  	 * @example
  	 *  onlyMimes : ["image"] - display all images
  	 *  onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash
  	 */
  	onlyMimes : [],
  
  	/**
  	 * Custom files sort rules.
  	 * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules
  	 *
  	 * @type {Object}
  	 * @example
  	 * sortRules : {
  	 *   name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); }
  	 * }
  	 */
  	sortRules : {},
  
  	/**
  	 * Default sort type.
  	 *
  	 * @type {String}
  	 */
  	sortType : 'name',
  	
  	/**
  	 * Default sort order.
  	 *
  	 * @type {String}
  	 * @default "asc"
  	 */
  	sortOrder : 'asc',
  	
  	/**
  	 * Display folders first?
  	 *
  	 * @type {Boolean}
  	 * @default true
  	 */
  	sortStickFolders : true,
  	
  	/**
  	 * If true - elFinder will formating dates itself, 
  	 * otherwise - backend date will be used.
  	 *
  	 * @type Boolean
  	 */
  	clientFormatDate : true,
  	
  	/**
  	 * Show UTC dates.
  	 * Required set clientFormatDate to true
  	 *
  	 * @type Boolean
  	 */
  	UTCDate : false,
  	
  	/**
  	 * File modification datetime format.
  	 * Value from selected language data  is used by default.
  	 * Set format here to overwrite it.
  	 *
  	 * @type String
  	 * @default  ""
  	 */
  	dateFormat : '',
  	
  	/**
  	 * File modification datetime format in form "Yesterday 12:23:01".
  	 * Value from selected language data is used by default.
  	 * Set format here to overwrite it.
  	 * Use $1 for "Today"/"Yesterday" placeholder
  	 *
  	 * @type String
  	 * @default  ""
  	 * @example "$1 H:m:i"
  	 */
  	fancyDateFormat : '',
  	
  	/**
  	 * Style of file mode at cwd-list, info dialog
  	 * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755))
  	 * 
  	 * @type {String}
  	 * @default 'both'
  	 */
  	fileModeStyle : 'both',
  	
  	/**
  	 * elFinder width
  	 *
  	 * @type String|Number
  	 * @default  "auto"
  	 */
  	width : 'auto',
  	
  	/**
  	 * elFinder height
  	 *
  	 * @type Number
  	 * @default  400
  	 */
  	height : 400,
  	
  	/**
  	 * Make elFinder resizable if jquery ui resizable available
  	 *
  	 * @type Boolean
  	 * @default  true
  	 */
  	resizable : true,
  	
  	/**
  	 * Timeout before open notifications dialogs
  	 *
  	 * @type Number
  	 * @default  500 (.5 sec)
  	 */
  	notifyDelay : 500,
  	
  	/**
  	 * Position CSS, Width of notifications dialogs
  	 *
  	 * @type Object
  	 * @default {position: {top : '12px', right : '12px'}, width : 280}
  	 * position: CSS object | null (null: position center & middle)
  	 */
  	notifyDialog : {position: {top : '12px', right : '12px'}, width : 280},
  	
  	/**
  	 * Allow shortcuts
  	 *
  	 * @type Boolean
  	 * @default  true
  	 */
  	allowShortcuts : true,
  	
  	/**
  	 * Remeber last opened dir to open it after reload or in next session
  	 *
  	 * @type Boolean
  	 * @default  true
  	 */
  	rememberLastDir : true,
  	
  	/**
  	 * Clear historys(elFinder) on reload(not browser) function
  	 * Historys was cleared on Reload function on elFinder 2.0 (value is true)
  	 * 
  	 * @type Boolean
  	 * @default  false
  	 */
  	reloadClearHistory : false,
  	
  	/**
  	 * Use browser native history with supported browsers
  	 *
  	 * @type Boolean
  	 * @default  true
  	 */
  	useBrowserHistory : true,
  	
  	/**
  	 * Lazy load config.
  	 * How many files display at once?
  	 *
  	 * @type Number
  	 * @default  50
  	 */
  	showFiles : 50,
  	
  	/**
  	 * Lazy load config.
  	 * Distance in px to cwd bottom edge to start display files
  	 *
  	 * @type Number
  	 * @default  50
  	 */
  	showThreshold : 50,
  	
  	/**
  	 * Additional rule to valid new file name.
  	 * By default not allowed empty names or '..'
  	 * This setting does not have a sense of security.
  	 *
  	 * @type false|RegExp|function
  	 * @default  false
  	 * @example
  	 *  disable names with spaces:
  	 *  validName : /^[^\s]+$/,
  	 */
  	validName : false,
  	
  	/**
  	 * Backup name suffix.
  	 *
  	 * @type String
  	 * @default  "~"
  	 */
  	backupSuffix : '~',
  	
  	/**
  	 * Sync content interval
  	 *
  	 * @type Number
  	 * @default  0 (do not sync)
  	 */
  	sync : 0,
  	
  	/**
  	 * Sync start on load if sync value >= 1000
  	 *
  	 * @type     Bool
  	 * @default  true
  	 */
  	syncStart : true,
  	
  	/**
  	 * How many thumbnails create in one request
  	 *
  	 * @type Number
  	 * @default  5
  	 */
  	loadTmbs : 5,
  	
  	/**
  	 * Cookie option for browsersdoes not suppot localStorage
  	 *
  	 * @type Object
  	 */
  	cookie         : {
  		expires : 30,
  		domain  : '',
  		path    : '/',
  		secure  : false
  	},
  	
  	/**
  	 * Contextmenu config
  	 *
  	 * @type Object
  	 */
  	contextmenu : {
  		// navbarfolder menu
  		navbar : ['open', 'download', '|', 'upload', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'],
  		// current directory menu
  		cwd    : ['reload', 'back', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'view', 'sort', 'colwidth', '|', 'info'],
  		// current directory file menu
  		files  : ['getfile', '|' ,'open', 'download', 'opendir', 'quicklook', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', '|', 'edit', 'rename', 'resize', '|', 'archive', 'extract', '|', 'places', 'info', 'chmod']
  	},
  
  	/**
  	 * elFinder node enable always
  	 * This value will set to `true` if <body> has elFinder node only
  	 * 
  	 * @type     Bool
  	 * @default  false
  	 */
  	enableAlways : false,
  
  	/**
  	 * Show window close confirm dialog
  	 * Value is which state to show
  	 * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData'
  	 * 
  	 * @type     Array
  	 * @default  ['hasNotifyDialog', 'editingFile']
  	 */
  	windowCloseConfirm : ['hasNotifyDialog', 'editingFile'],
  
  	/**
  	 * Debug config
  	 *
  	 * @type Array|Boolean
  	 */
  	// debug : true
  	debug : ['error', 'warning', 'event-destroy']
  };
  
  
  /*
   * File: /js/elFinder.history.js
   */
  
  /**
   * @class elFinder.history
   * Store visited folders
   * and provide "back" and "forward" methods
   *
   * @author Dmitry (dio) Levashov
   */
  elFinder.prototype.history = function(fm) {
  	var self = this,
  		/**
  		 * Update history on "open" event?
  		 *
  		 * @type Boolean
  		 */
  		update = true,
  		/**
  		 * Directories hashes storage
  		 *
  		 * @type Array
  		 */
  		history = [],
  		/**
  		 * Current directory index in history
  		 *
  		 * @type Number
  		 */
  		current,
  		/**
  		 * Clear history
  		 *
  		 * @return void
  		 */
  		reset = function() {
  			history = [fm.cwd().hash];
  			current = 0;
  			update  = true;
  		},
  		/**
  		 * Browser native history object
  		 */
  		nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null,
  		/**
  		 * Open prev/next folder
  		 *
  		 * @Boolen  open next folder?
  		 * @return jQuery.Deferred
  		 */
  		go = function(fwd) {
  			if ((fwd && self.canForward()) || (!fwd && self.canBack())) {
  				update = false;
  				return fm.exec('open', history[fwd ? ++current : --current]).fail(reset);
  			}
  			return $.Deferred().reject();
  		};
  	
  	/**
  	 * Return true if there is previous visited directories
  	 *
  	 * @return Boolen
  	 */
  	this.canBack = function() {
  		return current > 0;
  	}
  	
  	/**
  	 * Return true if can go forward
  	 *
  	 * @return Boolen
  	 */
  	this.canForward = function() {
  		return current < history.length - 1;
  	}
  	
  	/**
  	 * Go back
  	 *
  	 * @return void
  	 */
  	this.back = go;
  	
  	/**
  	 * Go forward
  	 *
  	 * @return void
  	 */
  	this.forward = function() {
  		return go(true);
  	}
  	
  	// bind to elfinder events
  	fm.open(function() {
  		var l = history.length,
  			cwd = fm.cwd().hash;
  
  		if (update) {
  			current >= 0 && l > current + 1 && history.splice(current+1);
  			history[history.length-1] != cwd && history.push(cwd);
  			current = history.length - 1;
  		}
  		update = true;
  
  		if (nativeHistory) {
  			if (! nativeHistory.state) {
  				nativeHistory.replaceState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd);
  			} else {
  				nativeHistory.state.thash != cwd && nativeHistory.pushState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd);
  			}
  		}
  	})
  	.reload(fm.options.reloadClearHistory && reset);
  	
  };
  
  
  /*
   * File: /js/elFinder.command.js
   */
  
  /**
   * elFinder command prototype
   *
   * @type  elFinder.command
   * @author  Dmitry (dio) Levashov
   */
  elFinder.prototype.command = function(fm) {
  
  	/**
  	 * elFinder instance
  	 *
  	 * @type  elFinder
  	 */
  	this.fm = fm;
  	
  	/**
  	 * Command name, same as class name
  	 *
  	 * @type  String
  	 */
  	this.name = '';
  	
  	/**
  	 * Command icon class name with out 'elfinder-button-icon-'
  	 * Use this.name if it is empty
  	 *
  	 * @type  String
  	 */
  	this.className = '';
  
  	/**
  	 * Short command description
  	 *
  	 * @type  String
  	 */
  	this.title = '';
  	
  	/**
  	 * Linked(Child) commands name
  	 * They are loaded together when tthis command is loaded.
  	 * 
  	 * @type  Array
  	 */
  	this.linkedCmds = [];
  	
  	/**
  	 * Current command state
  	 *
  	 * @example
  	 * this.state = -1; // command disabled
  	 * this.state = 0;  // command enabled
  	 * this.state = 1;  // command active (for example "fullscreen" command while elfinder in fullscreen mode)
  	 * @default -1
  	 * @type  Number
  	 */
  	this.state = -1;
  	
  	/**
  	 * If true, command can not be disabled by connector.
  	 * @see this.update()
  	 *
  	 * @type  Boolen
  	 */
  	this.alwaysEnabled = false;
  	
  	/**
  	 * If true, this means command was disabled by connector.
  	 * @see this.update()
  	 *
  	 * @type  Boolen
  	 */
  	this._disabled = false;
  	
  	this.disableOnSearch = false;
  	
  	this.updateOnSelect = true;
  	
  	/**
  	 * elFinder events defaults handlers.
  	 * Inside handlers "this" is current command object
  	 *
  	 * @type  Object
  	 */
  	this._handlers = {
  		enable  : function() { this.update(void(0), this.value); },
  		disable : function() { this.update(-1, this.value); },
  		'open reload load'    : function() { 
  			this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name));
  			this.update(void(0), this.value)
  			this.change(); 
  		}
  	};
  	
  	/**
  	 * elFinder events handlers.
  	 * Inside handlers "this" is current command object
  	 *
  	 * @type  Object
  	 */
  	this.handlers = {}
  	
  	/**
  	 * Shortcuts
  	 *
  	 * @type  Array
  	 */
  	this.shortcuts = [];
  	
  	/**
  	 * Command options
  	 *
  	 * @type  Object
  	 */
  	this.options = {ui : 'button'};
  	
  	/**
  	 * Prepare object -
  	 * bind events and shortcuts
  	 *
  	 * @return void
  	 */
  	this.setup = function(name, opts) {
  		var self = this,
  			fm   = this.fm, i, s, sc;
  
  		this.name      = name;
  		this.title     = fm.messages['cmd'+name] ? fm.i18n('cmd'+name) : name, 
  		this.options   = $.extend({}, this.options, opts);
  		this.listeners = [];
  
  		if (opts.shortcuts) {
  			if (typeof opts.shortcuts === 'function') {
  				sc = opts.shortcuts(this.fm, this.shortcuts);
  			} else if ($.isArray(opts.shortcuts)) {
  				sc = opts.shortcuts;
  			}
  			this.shortcuts = sc || [];
  		}
  
  		if (this.updateOnSelect) {
  			this._handlers.select = function() { this.update(void(0), this.value); }
  		}
  
  		$.each($.extend({}, self._handlers, self.handlers), function(cmd, handler) {
  			fm.bind(cmd, $.proxy(handler, self));
  		});
  
  		for (i = 0; i < this.shortcuts.length; i++) {
  			s = this.shortcuts[i];
  			s.callback = $.proxy(s.callback || function() { this.exec() }, this);
  			!s.description && (s.description = this.title);
  			fm.shortcut(s);
  		}
  
  		if (this.disableOnSearch) {
  			fm.bind('search searchend', function(e) {
  				self._disabled = e.type == 'search';
  				self.update(void(0), self.value);
  			});
  		}
  
  		this.init();
  	}
  
  	/**
  	 * Command specific init stuffs
  	 *
  	 * @return void
  	 */
  	this.init = function() { }
  
  	/**
  	 * Exec command
  	 *
  	 * @param  Array         target files hashes
  	 * @param  Array|Object  command value
  	 * @return $.Deferred
  	 */
  	this.exec = function(files, opts) { 
  		return $.Deferred().reject(); 
  	}
  	
  	/**
  	 * Return true if command disabled.
  	 *
  	 * @return Boolen
  	 */
  	this.disabled = function() {
  		return this.state < 0;
  	}
  	
  	/**
  	 * Return true if command enabled.
  	 *
  	 * @return Boolen
  	 */
  	this.enabled = function() {
  		return this.state > -1;
  	}
  	
  	/**
  	 * Return true if command active.
  	 *
  	 * @return Boolen
  	 */
  	this.active = function() {
  		return this.state > 0;
  	}
  	
  	/**
  	 * Return current command state.
  	 * Must be overloaded in most commands
  	 *
  	 * @return Number
  	 */
  	this.getstate = function() {
  		return -1;
  	}
  	
  	/**
  	 * Update command state/value
  	 * and rize 'change' event if smth changed
  	 *
  	 * @param  Number  new state or undefined to auto update state
  	 * @param  mixed   new value
  	 * @return void
  	 */
  	this.update = function(s, v) {
  		var state = this.state,
  			value = this.value;
  
  		if (this._disabled) {
  			this.state = -1;
  		} else {
  			this.state = s !== void(0) ? s : this.getstate();
  		}
  
  		this.value = v;
  		
  		if (state != this.state || value != this.value) {
  			this.change();
  		}
  	}
  	
  	/**
  	 * Bind handler / fire 'change' event.
  	 *
  	 * @param  Function|undefined  event callback
  	 * @return void
  	 */
  	this.change = function(c) {
  		var cmd, i;
  		
  		if (typeof(c) === 'function') {
  			this.listeners.push(c);			
  		} else {
  			for (i = 0; i < this.listeners.length; i++) {
  				cmd = this.listeners[i];
  				try {
  					cmd(this.state, this.value);
  				} catch (e) {
  					this.fm.debug('error', e)
  				}
  			}
  		}
  		return this;
  	}
  	
  
  	/**
  	 * With argument check given files hashes and return list of existed files hashes.
  	 * Without argument return selected files hashes.
  	 *
  	 * @param  Array|String|void  hashes
  	 * @return Array
  	 */
  	this.hashes = function(hashes) {
  		return hashes
  			? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? hash : null; })
  			: fm.selected();
  	}
  	
  	/**
  	 * Return only existed files from given fils hashes | selected files
  	 *
  	 * @param  Array|String|void  hashes
  	 * @return Array
  	 */
  	this.files = function(hashes) {
  		var fm = this.fm;
  		
  		return hashes
  			? $.map($.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null })
  			: fm.selectedFiles();
  	}
  };
  
  
  /*
   * File: /js/elFinder.resources.js
   */
  
  /**
   * elFinder resources registry.
   * Store shared data
   *
   * @type Object
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.resources = {
  	'class' : {
  		hover       : 'ui-state-hover',
  		active      : 'ui-state-active',
  		disabled    : 'ui-state-disabled',
  		draggable   : 'ui-draggable',
  		droppable   : 'ui-droppable',
  		adroppable  : 'elfinder-droppable-active',
  		cwdfile     : 'elfinder-cwd-file',
  		cwd         : 'elfinder-cwd',
  		tree        : 'elfinder-tree',
  		treeroot    : 'elfinder-navbar-root',
  		navdir      : 'elfinder-navbar-dir',
  		navdirwrap  : 'elfinder-navbar-dir-wrapper',
  		navarrow    : 'elfinder-navbar-arrow',
  		navsubtree  : 'elfinder-navbar-subtree',
  		navcollapse : 'elfinder-navbar-collapsed',
  		navexpand   : 'elfinder-navbar-expanded',
  		treedir     : 'elfinder-tree-dir',
  		placedir    : 'elfinder-place-dir',
  		searchbtn   : 'elfinder-button-search',
  		editing     : 'elfinder-to-editing'
  	},
  	tpl : {
  		perms      : '<span class="elfinder-perms"/>',
  		lock       : '<span class="elfinder-lock"/>',
  		symlink    : '<span class="elfinder-symlink"/>',
  		navicon    : '<span class="elfinder-nav-icon"/>',
  		navspinner : '<span class="elfinder-navbar-spinner"/>',
  		navdir     : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree"/></div>',
  		placedir   : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}" title="{title}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree"/></div>'
  		
  	},
  	
  	mimes : {
  		text : [
  			'application/x-empty',
  			'application/javascript', 
  			'application/xhtml+xml', 
  			'audio/x-mp3-playlist', 
  			'application/x-web-config',
  			'application/docbook+xml',
  			'application/x-php',
  			'application/x-perl',
  			'application/x-awk',
  			'application/x-config',
  			'application/x-csh',
  			'application/xml'
  		]
  	},
  	
  	mixin : {
  		make : function() {
  			var fm   = this.fm,
  				cmd  = this.name,
  				wz   = fm.getUI('workzone'),
  				cwd  = fm.getUI('cwd'),
  				tarea= (fm.storage('view') != 'list'),
  				sel  = fm.selected(),
  				move = this.move || false,
  				empty= wz.hasClass('elfinder-cwd-wrapper-empty'),
  				rest = function(){
  					if (!overlay.is(':hidden')) {
  						overlay.addClass('ui-front')
  							.elfinderoverlay('hide')
  							.off('click', cancel);
  					}
  					node.removeClass('ui-front').css('position', '');
  					if (tarea) {
  						nnode.css('max-height', '');
  					} else {
  						pnode.css('width', '')
  							.parent('td').css('overflow', '');
  					}
  				}, colwidth,
  				dfrd = $.Deferred()
  					.fail(function(error) {
  						empty && wz.addClass('elfinder-cwd-wrapper-empty');
  						if (sel) {
  							move && fm.trigger('unlockfiles', {files: sel});
  							fm.clipboard([]);
  							fm.trigger('selectfiles', { files: sel })
  						}
  						error && fm.error(error);
  					})
  					.always(function() {
  						rest();
  						input.remove();
  						node.remove();
  						fm.enable();
  						fm.trigger('resMixinMake');
  					}),
  				id    = 'tmp_'+parseInt(Math.random()*100000),
  				phash = fm.cwd().hash,
  				date = new Date(),
  				file   = {
  					hash  : id,
  					name  : fm.uniqueName(this.prefix),
  					mime  : this.mime,
  					read  : true,
  					write : true,
  					date  : 'Today '+date.getHours()+':'+date.getMinutes(),
  					move  : move
  				},
  				data = this.data || {},
  				node = cwd.trigger('create.'+fm.namespace, file).find('#'+fm.cwdHash2Id(id))
  					.on('unselect.'+fm.namespace, function() {
  						setTimeout(function() {
  							input && input.blur();
  						}, 50);
  					}),
  				nnode, pnode,
  				overlay = fm.getUI().children('.elfinder-overlay'),
  				cancel = function(e) { 
  					if (! inError) {
  						input.remove();
  						node.remove();
  						e.stopPropagation();
  						dfrd.reject();
  					}
  				},
  				input = $(tarea? '<textarea/>' : '<input type="text"/>')
  					.on('keyup text', function(){
  						if (tarea) {
  							this.style.height = '1px';
  							this.style.height = this.scrollHeight + 'px';
  						} else if (colwidth) {
  							this.style.width = colwidth + 'px';
  							if (this.scrollWidth > colwidth) {
  								this.style.width = this.scrollWidth + 10 + 'px';
  							}
  						}
  					})
  					.keydown(function(e) {
  						e.stopImmediatePropagation();
  						if (e.keyCode == $.ui.keyCode.ESCAPE) {
  							dfrd.reject();
  						} else if (e.keyCode == $.ui.keyCode.ENTER) {
  							input.blur();
  						}
  					})
  					.mousedown(function(e) {
  						e.stopPropagation();
  					})
  					.blur(function() {
  						var name   = $.trim(input.val()),
  							parent = input.parent(),
  							valid  = true,
  							cut;
  
  						if (!inError && parent.length) {
  
  							if (fm.options.validName && fm.options.validName.test) {
  								try {
  									valid = fm.options.validName.test(name);
  								} catch(e) {
  									valid = false;
  								}
  							}
  							if (!name || name === '..' || !valid) {
  								inError = true;
  								fm.error('errInvName', {modal: true, close: select});
  								return false;
  							}
  							if (fm.fileByName(name, phash)) {
  								inError = true;
  								fm.error(['errExists', name], {modal: true, close: select});
  								return false;
  							}
  
  							cut = (sel && move)? fm.exec('cut', sel) : null;
  
  							$.when(cut)
  							.done(function() {
  								rest();
  								parent.html(fm.escape(name));
  
  								fm.lockfiles({files : [id]});
  
  								fm.request({
  										data        : $.extend({cmd : cmd, name : name, target : phash}, data || {}), 
  										notify      : {type : cmd, cnt : 1},
  										preventFail : true,
  										syncOnFail  : true
  									})
  									.fail(function(error) {
  										dfrd.reject(error);
  									})
  									.done(function(data) {
  										dfrd.resolve(data);
  										if (data.added && data.added[0]) {
  											var dirhash = data.added[0].hash,
  												newItem = cwd.find('#'+fm.cwdHash2Id(dirhash));
  											if (sel && move) {
  												fm.exec('paste', dirhash).done(function() {
  													fm.trigger('selectfiles', { files: [dirhash] });
  												});
  											}
  											if (newItem.length) {
  												newItem.trigger('scrolltoview');
  											}
  										}
  									});
  							})
  							.fail(function() {
  								dfrd.reject();
  							});
  						}
  					}),
  				select = function() {
  					var name = input.val().replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, '');
  					inError = false;
  					if (fm.UA.Mobile) {
  						overlay.on('click', cancel)
  							.removeClass('ui-front').elfinderoverlay('show');
  					}
  					input.select().focus();
  					input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  				},
  				inError = false;
  
  			if (this.disabled() || !node.length) {
  				return dfrd.reject();
  			}
  
  			empty && wz.removeClass('elfinder-cwd-wrapper-empty');
  			nnode = node.find('.elfinder-cwd-filename');
  			pnode = nnode.parent();
  			node.css('position', 'relative').addClass('ui-front');
  			if (tarea) {
  				nnode.css('max-height', 'none');
  			} else {
  				colwidth = pnode.width();
  				pnode.width(colwidth - 15)
  					.parent('td').css('overflow', 'visible');
  			}
  			nnode.empty('').append(input.val(file.name));
  			
  			input.trigger('keyup');
  			select();
  
  			return dfrd;
  
  		}
  	},
  	blink: function(elm, mode) {
  		mode = mode || 'slowonce';
  		if (mode === 'slowonce') {
  			elm.stop(true, true).delay(250).fadeIn(750).delay(500).fadeOut(3500);
  		}
  	}
  };
  
  
  /*
   * File: /js/jquery.dialogelfinder.js
   */
  
  /**
   * @class dialogelfinder - open elFinder in dialog window
   *
   * @param  Object  elFinder options with dialog options
   * @example
   * $(selector).dialogelfinder({
   *     // some elfinder options
   *     title          : 'My files', // dialog title, default = "Files"
   *     width          : 850,        // dialog width, default 840
   *     autoOpen       : false,      // if false - dialog will not be opened after init, default = true
   *     destroyOnClose : true        // destroy elFinder on close dialog, default = false
   * })
   * @author Dmitry (dio) Levashov
   **/
  $.fn.dialogelfinder = function(opts) {
  	var position = 'elfinderPosition',
  		destroy  = 'elfinderDestroyOnClose';
  	
  	this.not('.elfinder').each(function() {
  
  		
  		var doc     = $(document),
  			toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'),
  			button  = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>')
  				.appendTo(toolbar)
  				.click(function(e) {
  					e.preventDefault();
  					
  					node.dialogelfinder('close');
  				}),
  			node    = $(this).addClass('dialogelfinder')
  				.css('position', 'absolute')
  				.hide()
  				.appendTo('body')
  				.draggable({ handle : '.dialogelfinder-drag',
  					     containment : 'window' })
  				.elfinder(opts)
  				.prepend(toolbar),
  			elfinder = node.elfinder('instance');
  		
  		
  		node.width(parseInt(node.width()) || 840) // fix width if set to "auto"
  			.data(destroy, !!opts.destroyOnClose)
  			.find('.elfinder-toolbar').removeClass('ui-corner-top');
  		
  		opts.position && node.data(position, opts.position);
  		
  		opts.autoOpen !== false && $(this).dialogelfinder('open');
  
  	});
  	
  	if (opts == 'open') {
  		var node = $(this),
  			pos  = node.data(position) || {
  				top  : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)),
  				left : parseInt($(document).scrollLeft() + ($(window).width() < node.width()  ? 2 : ($(window).width()  - node.width())/2))
  			};
  
  		if (node.is(':hidden')) {
  			node.addClass('ui-front').css(pos).show().trigger('resize');
  
  			setTimeout(function() {
  				// fix resize icon position and make elfinder active
  				node.trigger('resize').mousedown();
  			}, 200);
  		}
  	} else if (opts == 'close') {
  		var node = $(this).removeClass('ui-front');
  			
  		if (node.is(':visible')) {
  			!!node.data(destroy)
  				? node.elfinder('destroy').remove()
  				: node.elfinder('close');
  		}
  	} else if (opts == 'instance') {
  		return $(this).getElFinder();
  	}
  
  	return this;
  };
  
  
  /*
   * File: /js/i18n/elfinder.en.js
   */
  
  /**
   * English translation
   * @author Troex Nevelin <troex@fury.scancode.ru>
   * @version 2016-06-30
   */
  if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') {
  	elFinder.prototype.i18.en = {
  		translator : 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;',
  		language   : 'English',
  		direction  : 'ltr',
  		dateFormat : 'M d, Y h:i A', // Mar 13, 2012 05:27 PM
  		fancyDateFormat : '$1 h:i A', // will produce smth like: Today 12:25 PM
  		messages   : {
  
  			/********************************** errors **********************************/
  			'error'                : 'Error',
  			'errUnknown'           : 'Unknown error.',
  			'errUnknownCmd'        : 'Unknown command.',
  			'errJqui'              : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.',
  			'errNode'              : 'elFinder requires DOM Element to be created.',
  			'errURL'               : 'Invalid elFinder configuration! URL option is not set.',
  			'errAccess'            : 'Access denied.',
  			'errConnect'           : 'Unable to connect to backend.',
  			'errAbort'             : 'Connection aborted.',
  			'errTimeout'           : 'Connection timeout.',
  			'errNotFound'          : 'Backend not found.',
  			'errResponse'          : 'Invalid backend response.',
  			'errConf'              : 'Invalid backend configuration.',
  			'errJSON'              : 'PHP JSON module not installed.',
  			'errNoVolumes'         : 'Readable volumes not available.',
  			'errCmdParams'         : 'Invalid parameters for command "$1".',
  			'errDataNotJSON'       : 'Data is not JSON.',
  			'errDataEmpty'         : 'Data is empty.',
  			'errCmdReq'            : 'Backend request requires command name.',
  			'errOpen'              : 'Unable to open "$1".',
  			'errNotFolder'         : 'Object is not a folder.',
  			'errNotFile'           : 'Object is not a file.',
  			'errRead'              : 'Unable to read "$1".',
  			'errWrite'             : 'Unable to write into "$1".',
  			'errPerm'              : 'Permission denied.',
  			'errLocked'            : '"$1" is locked and can not be renamed, moved or removed.',
  			'errExists'            : 'File named "$1" already exists.',
  			'errInvName'           : 'Invalid file name.',
  			'errFolderNotFound'    : 'Folder not found.',
  			'errFileNotFound'      : 'File not found.',
  			'errTrgFolderNotFound' : 'Target folder "$1" not found.',
  			'errPopup'             : 'Browser prevented opening popup window. To open file enable it in browser options.',
  			'errMkdir'             : 'Unable to create folder "$1".',
  			'errMkfile'            : 'Unable to create file "$1".',
  			'errRename'            : 'Unable to rename "$1".',
  			'errCopyFrom'          : 'Copying files from volume "$1" not allowed.',
  			'errCopyTo'            : 'Copying files to volume "$1" not allowed.',
  			'errMkOutLink'         : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015
  			'errUpload'            : 'Upload error.',  // old name - errUploadCommon
  			'errUploadFile'        : 'Unable to upload "$1".', // old name - errUpload
  			'errUploadNoFiles'     : 'No files found for upload.',
  			'errUploadTotalSize'   : 'Data exceeds the maximum allowed size.', // old name - errMaxSize
  			'errUploadFileSize'    : 'File exceeds maximum allowed size.', //  old name - errFileMaxSize
  			'errUploadMime'        : 'File type not allowed.',
  			'errUploadTransfer'    : '"$1" transfer error.',
  			'errUploadTemp'        : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015
  			'errNotReplace'        : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new
  			'errReplace'           : 'Unable to replace "$1".',
  			'errSave'              : 'Unable to save "$1".',
  			'errCopy'              : 'Unable to copy "$1".',
  			'errMove'              : 'Unable to move "$1".',
  			'errCopyInItself'      : 'Unable to copy "$1" into itself.',
  			'errRm'                : 'Unable to remove "$1".',
  			'errRmSrc'             : 'Unable remove source file(s).',
  			'errExtract'           : 'Unable to extract files from "$1".',
  			'errArchive'           : 'Unable to create archive.',
  			'errArcType'           : 'Unsupported archive type.',
  			'errNoArchive'         : 'File is not archive or has unsupported archive type.',
  			'errCmdNoSupport'      : 'Backend does not support this command.',
  			'errReplByChild'       : 'The folder "$1" can\'t be replaced by an item it contains.',
  			'errArcSymlinks'       : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012
  			'errArcMaxSize'        : 'Archive files exceeds maximum allowed size.',
  			'errResize'            : 'Unable to resize "$1".',
  			'errResizeDegree'      : 'Invalid rotate degree.',  // added 7.3.2013
  			'errResizeRotate'      : 'Unable to rotate image.',  // added 7.3.2013
  			'errResizeSize'        : 'Invalid image size.',  // added 7.3.2013
  			'errResizeNoChange'    : 'Image size not changed.',  // added 7.3.2013
  			'errUsupportType'      : 'Unsupported file type.',
  			'errNotUTF8Content'    : 'File "$1" is not in UTF-8 and cannot be edited.',  // added 9.11.2011
  			'errNetMount'          : 'Unable to mount "$1".', // added 17.04.2012
  			'errNetMountNoDriver'  : 'Unsupported protocol.',     // added 17.04.2012
  			'errNetMountFailed'    : 'Mount failed.',         // added 17.04.2012
  			'errNetMountHostReq'   : 'Host required.', // added 18.04.2012
  			'errSessionExpires'    : 'Your session has expired due to inactivity.',
  			'errCreatingTempDir'   : 'Unable to create temporary directory: "$1"',
  			'errFtpDownloadFile'   : 'Unable to download file from FTP: "$1"',
  			'errFtpUploadFile'     : 'Unable to upload file to FTP: "$1"',
  			'errFtpMkdir'          : 'Unable to create remote directory on FTP: "$1"',
  			'errArchiveExec'       : 'Error while archiving files: "$1"',
  			'errExtractExec'       : 'Error while extracting files: "$1"',
  			'errNetUnMount'        : 'Unable to unmount', // from v2.1 added 30.04.2012
  			'errConvUTF8'          : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014
  			'errFolderUpload'      : 'Try Google Chrome, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015
  			'errSearchTimeout'     : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016
  			'errReauthRequire'     : 'Re-authorization is required.', // from v2.1.10 added 3.24.2016
  
  			/******************************* commands names ********************************/
  			'cmdarchive'   : 'Create archive',
  			'cmdback'      : 'Back',
  			'cmdcopy'      : 'Copy',
  			'cmdcut'       : 'Cut',
  			'cmddownload'  : 'Download',
  			'cmdduplicate' : 'Duplicate',
  			'cmdedit'      : 'Edit file',
  			'cmdextract'   : 'Extract files from archive',
  			'cmdforward'   : 'Forward',
  			'cmdgetfile'   : 'Select files',
  			'cmdhelp'      : 'About this software',
  			'cmdhome'      : 'Home',
  			'cmdinfo'      : 'Get info',
  			'cmdmkdir'     : 'New folder',
  			'cmdmkdirin'   : 'Into New Folder', // from v2.1.7 added 19.2.2016
  			'cmdmkfile'    : 'New text file',
  			'cmdopen'      : 'Open',
  			'cmdpaste'     : 'Paste',
  			'cmdquicklook' : 'Preview',
  			'cmdreload'    : 'Reload',
  			'cmdrename'    : 'Rename',
  			'cmdrm'        : 'Delete',
  			'cmdsearch'    : 'Find files',
  			'cmdup'        : 'Go to parent directory',
  			'cmdupload'    : 'Upload files',
  			'cmdview'      : 'View',
  			'cmdresize'    : 'Resize & Rotate',
  			'cmdsort'      : 'Sort',
  			'cmdnetmount'  : 'Mount network volume', // added 18.04.2012
  			'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012
  			'cmdplaces'    : 'To Places', // added 28.12.2014
  			'cmdchmod'     : 'Change mode', // from v2.1 added 20.6.2015
  			'cmdopendir'   : 'Open a folder', // from v2.1 added 13.1.2016
  			'cmdcolwidth'  : 'Reset column width', // from v2.1.13 added 12.06.2016
  
  			/*********************************** buttons ***********************************/
  			'btnClose'  : 'Close',
  			'btnSave'   : 'Save',
  			'btnRm'     : 'Remove',
  			'btnApply'  : 'Apply',
  			'btnCancel' : 'Cancel',
  			'btnNo'     : 'No',
  			'btnYes'    : 'Yes',
  			'btnMount'  : 'Mount',  // added 18.04.2012
  			'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012
  			'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012
  			'btnConv'   : 'Convert', // from v2.1 added 08.04.2014
  			'btnCwd'    : 'Here',      // from v2.1 added 22.5.2015
  			'btnVolume' : 'Volume',    // from v2.1 added 22.5.2015
  			'btnAll'    : 'All',       // from v2.1 added 22.5.2015
  			'btnMime'   : 'MIME Type', // from v2.1 added 22.5.2015
  			'btnFileName':'Filename',  // from v2.1 added 22.5.2015
  			'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015
  			'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015
  
  			/******************************** notifications ********************************/
  			'ntfopen'     : 'Open folder',
  			'ntffile'     : 'Open file',
  			'ntfreload'   : 'Reload folder content',
  			'ntfmkdir'    : 'Creating directory',
  			'ntfmkfile'   : 'Creating files',
  			'ntfrm'       : 'Delete files',
  			'ntfcopy'     : 'Copy files',
  			'ntfmove'     : 'Move files',
  			'ntfprepare'  : 'Prepare to copy files',
  			'ntfrename'   : 'Rename files',
  			'ntfupload'   : 'Uploading files',
  			'ntfdownload' : 'Downloading files',
  			'ntfsave'     : 'Save files',
  			'ntfarchive'  : 'Creating archive',
  			'ntfextract'  : 'Extracting files from archive',
  			'ntfsearch'   : 'Searching files',
  			'ntfresize'   : 'Resizing images',
  			'ntfsmth'     : 'Doing something',
  			'ntfloadimg'  : 'Loading image',
  			'ntfnetmount' : 'Mounting network volume', // added 18.04.2012
  			'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012
  			'ntfdim'      : 'Acquiring image dimension', // added 20.05.2013
  			'ntfreaddir'  : 'Reading folder infomation', // from v2.1 added 01.07.2013
  			'ntfurl'      : 'Getting URL of link', // from v2.1 added 11.03.2014
  			'ntfchmod'    : 'Changing file mode', // from v2.1 added 20.6.2015
  			'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015
  			'ntfzipdl'    : 'Creating a file for download', // from v2.1.7 added 23.1.2016
  
  			/************************************ dates **********************************/
  			'dateUnknown' : 'unknown',
  			'Today'       : 'Today',
  			'Yesterday'   : 'Yesterday',
  			'msJan'       : 'Jan',
  			'msFeb'       : 'Feb',
  			'msMar'       : 'Mar',
  			'msApr'       : 'Apr',
  			'msMay'       : 'May',
  			'msJun'       : 'Jun',
  			'msJul'       : 'Jul',
  			'msAug'       : 'Aug',
  			'msSep'       : 'Sep',
  			'msOct'       : 'Oct',
  			'msNov'       : 'Nov',
  			'msDec'       : 'Dec',
  			'January'     : 'January',
  			'February'    : 'February',
  			'March'       : 'March',
  			'April'       : 'April',
  			'May'         : 'May',
  			'June'        : 'June',
  			'July'        : 'July',
  			'August'      : 'August',
  			'September'   : 'September',
  			'October'     : 'October',
  			'November'    : 'November',
  			'December'    : 'December',
  			'Sunday'      : 'Sunday',
  			'Monday'      : 'Monday',
  			'Tuesday'     : 'Tuesday',
  			'Wednesday'   : 'Wednesday',
  			'Thursday'    : 'Thursday',
  			'Friday'      : 'Friday',
  			'Saturday'    : 'Saturday',
  			'Sun'         : 'Sun',
  			'Mon'         : 'Mon',
  			'Tue'         : 'Tue',
  			'Wed'         : 'Wed',
  			'Thu'         : 'Thu',
  			'Fri'         : 'Fri',
  			'Sat'         : 'Sat',
  
  			/******************************** sort variants ********************************/
  			'sortname'          : 'by name',
  			'sortkind'          : 'by kind',
  			'sortsize'          : 'by size',
  			'sortdate'          : 'by date',
  			'sortFoldersFirst'  : 'Folders first',
  			'sortperm'          : 'by permission', // from v2.1.13 added 13.06.2016
  			'sortmode'          : 'by mode',       // from v2.1.13 added 13.06.2016
  			'sortowner'         : 'by owner',      // from v2.1.13 added 13.06.2016
  			'sortgroup'         : 'by group',      // from v2.1.13 added 13.06.2016
  
  			/********************************** new items **********************************/
  			'untitled file.txt' : 'NewFile.txt', // added 10.11.2015
  			'untitled folder'   : 'NewFolder',   // added 10.11.2015
  			'Archive'           : 'NewArchive',  // from v2.1 added 10.11.2015
  
  			/********************************** messages **********************************/
  			'confirmReq'      : 'Confirmation required',
  			'confirmRm'       : 'Are you sure you want to remove files?<br/>This cannot be undone!',
  			'confirmRepl'     : 'Replace old file with new one?',
  			'confirmConvUTF8' : 'Not in UTF-8<br/>Convert to UTF-8?<br/>Contents become UTF-8 by saving after conversion.', // from v2.1 added 08.04.2014
  			'confirmNotSave'  : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015
  			'apllyAll'        : 'Apply to all',
  			'name'            : 'Name',
  			'size'            : 'Size',
  			'perms'           : 'Permissions',
  			'modify'          : 'Modified',
  			'kind'            : 'Kind',
  			'read'            : 'read',
  			'write'           : 'write',
  			'noaccess'        : 'no access',
  			'and'             : 'and',
  			'unknown'         : 'unknown',
  			'selectall'       : 'Select all files',
  			'selectfiles'     : 'Select file(s)',
  			'selectffile'     : 'Select first file',
  			'selectlfile'     : 'Select last file',
  			'viewlist'        : 'List view',
  			'viewicons'       : 'Icons view',
  			'places'          : 'Places',
  			'calc'            : 'Calculate',
  			'path'            : 'Path',
  			'aliasfor'        : 'Alias for',
  			'locked'          : 'Locked',
  			'dim'             : 'Dimensions',
  			'files'           : 'Files',
  			'folders'         : 'Folders',
  			'items'           : 'Items',
  			'yes'             : 'yes',
  			'no'              : 'no',
  			'link'            : 'Link',
  			'searcresult'     : 'Search results',
  			'selected'        : 'selected items',
  			'about'           : 'About',
  			'shortcuts'       : 'Shortcuts',
  			'help'            : 'Help',
  			'webfm'           : 'Web file manager',
  			'ver'             : 'Version',
  			'protocolver'     : 'protocol version',
  			'homepage'        : 'Project home',
  			'docs'            : 'Documentation',
  			'github'          : 'Fork us on Github',
  			'twitter'         : 'Follow us on twitter',
  			'facebook'        : 'Join us on facebook',
  			'team'            : 'Team',
  			'chiefdev'        : 'chief developer',
  			'developer'       : 'developer',
  			'contributor'     : 'contributor',
  			'maintainer'      : 'maintainer',
  			'translator'      : 'translator',
  			'icons'           : 'Icons',
  			'dontforget'      : 'and don\'t forget to take your towel',
  			'shortcutsof'     : 'Shortcuts disabled',
  			'dropFiles'       : 'Drop files here',
  			'or'              : 'or',
  			'selectForUpload' : 'Select files to upload',
  			'moveFiles'       : 'Move files',
  			'copyFiles'       : 'Copy files',
  			'rmFromPlaces'    : 'Remove from places',
  			'aspectRatio'     : 'Aspect ratio',
  			'scale'           : 'Scale',
  			'width'           : 'Width',
  			'height'          : 'Height',
  			'resize'          : 'Resize',
  			'crop'            : 'Crop',
  			'rotate'          : 'Rotate',
  			'rotate-cw'       : 'Rotate 90 degrees CW',
  			'rotate-ccw'      : 'Rotate 90 degrees CCW',
  			'degree'          : '°',
  			'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012
  			'protocol'            : 'Protocol', // added 18.04.2012
  			'host'                : 'Host', // added 18.04.2012
  			'port'                : 'Port', // added 18.04.2012
  			'user'                : 'User', // added 18.04.2012
  			'pass'                : 'Password', // added 18.04.2012
  			'confirmUnmount'      : 'Are you unmount $1?',  // from v2.1 added 30.04.2012
  			'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012
  			'dropPasteFiles'  : 'Drop or Paste files here', // from v2.1 added 07.04.2014
  			'encoding'        : 'Encoding', // from v2.1 added 19.12.2014
  			'locale'          : 'Locale',   // from v2.1 added 19.12.2014
  			'searchTarget'    : 'Target: $1',                // from v2.1 added 22.5.2015
  			'searchMime'      : 'Search by input MIME Type', // from v2.1 added 22.5.2015
  			'owner'           : 'Owner', // from v2.1 added 20.6.2015
  			'group'           : 'Group', // from v2.1 added 20.6.2015
  			'other'           : 'Other', // from v2.1 added 20.6.2015
  			'execute'         : 'Execute', // from v2.1 added 20.6.2015
  			'perm'            : 'Permission', // from v2.1 added 20.6.2015
  			'mode'            : 'Mode', // from v2.1 added 20.6.2015
  			'emptyFolder'     : 'Folder is empty', // from v2.1.6 added 30.12.2015
  			'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015
  			'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015
  			'quality'         : 'Quality', // from v2.1.6 added 5.1.2016
  			'autoSync'        : 'Auto sync',  // from v2.1.6 added 10.1.2016
  			'moveUp'          : 'Move up',  // from v2.1.6 added 18.1.2016
  			'getLink'         : 'Get URL link', // from v2.1.7 added 9.2.2016
  			'selectedItems'   : 'Selected items ($1)', // from v2.1.7 added 2.19.2016
  			'folderId'        : 'Folder ID', // from v2.1.10 added 3.25.2016
  			'offlineAccess'   : 'Allow offline access', // from v2.1.10 added 3.25.2016
  			'reAuth'          : 'To re-authenticate', // from v2.1.10 added 3.25.2016
  			'nowLoading'      : 'Now loading...', // from v2.1.12 added 4.26.2016
  			'openMulti'       : 'Open multiple files', // from v2.1.12 added 5.14.2016
  			'openMultiConfirm': 'You are trying to open the $1 files. Are you sure you want to open in browser?', // from v2.1.12 added 5.14.2016
  			'emptySearch'     : 'Search results is empty', // from v2.1.12 added 5.16.2016
  			'editingFile'     : 'It is editing a file.', // from v2.1.13 added 6.3.2016
  			'hasSelected'     : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016
  			'hasClipboard'    : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016
  			'incSearchOnly'   : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016
  
  			/********************************** mimetypes **********************************/
  			'kindUnknown'     : 'Unknown',
  			'kindFolder'      : 'Folder',
  			'kindAlias'       : 'Alias',
  			'kindAliasBroken' : 'Broken alias',
  			// applications
  			'kindApp'         : 'Application',
  			'kindPostscript'  : 'Postscript document',
  			'kindMsOffice'    : 'Microsoft Office document',
  			'kindMsWord'      : 'Microsoft Word document',
  			'kindMsExcel'     : 'Microsoft Excel document',
  			'kindMsPP'        : 'Microsoft Powerpoint presentation',
  			'kindOO'          : 'Open Office document',
  			'kindAppFlash'    : 'Flash application',
  			'kindPDF'         : 'Portable Document Format (PDF)',
  			'kindTorrent'     : 'Bittorrent file',
  			'kind7z'          : '7z archive',
  			'kindTAR'         : 'TAR archive',
  			'kindGZIP'        : 'GZIP archive',
  			'kindBZIP'        : 'BZIP archive',
  			'kindXZ'          : 'XZ archive',
  			'kindZIP'         : 'ZIP archive',
  			'kindRAR'         : 'RAR archive',
  			'kindJAR'         : 'Java JAR file',
  			'kindTTF'         : 'True Type font',
  			'kindOTF'         : 'Open Type font',
  			'kindRPM'         : 'RPM package',
  			// texts
  			'kindText'        : 'Text document',
  			'kindTextPlain'   : 'Plain text',
  			'kindPHP'         : 'PHP source',
  			'kindCSS'         : 'Cascading style sheet',
  			'kindHTML'        : 'HTML document',
  			'kindJS'          : 'Javascript source',
  			'kindRTF'         : 'Rich Text Format',
  			'kindC'           : 'C source',
  			'kindCHeader'     : 'C header source',
  			'kindCPP'         : 'C++ source',
  			'kindCPPHeader'   : 'C++ header source',
  			'kindShell'       : 'Unix shell script',
  			'kindPython'      : 'Python source',
  			'kindJava'        : 'Java source',
  			'kindRuby'        : 'Ruby source',
  			'kindPerl'        : 'Perl script',
  			'kindSQL'         : 'SQL source',
  			'kindXML'         : 'XML document',
  			'kindAWK'         : 'AWK source',
  			'kindCSV'         : 'Comma separated values',
  			'kindDOCBOOK'     : 'Docbook XML document',
  			'kindMarkdown'    : 'Markdown text', // added 20.7.2015
  			// images
  			'kindImage'       : 'Image',
  			'kindBMP'         : 'BMP image',
  			'kindJPEG'        : 'JPEG image',
  			'kindGIF'         : 'GIF Image',
  			'kindPNG'         : 'PNG Image',
  			'kindTIFF'        : 'TIFF image',
  			'kindTGA'         : 'TGA image',
  			'kindPSD'         : 'Adobe Photoshop image',
  			'kindXBITMAP'     : 'X bitmap image',
  			'kindPXM'         : 'Pixelmator image',
  			// media
  			'kindAudio'       : 'Audio media',
  			'kindAudioMPEG'   : 'MPEG audio',
  			'kindAudioMPEG4'  : 'MPEG-4 audio',
  			'kindAudioMIDI'   : 'MIDI audio',
  			'kindAudioOGG'    : 'Ogg Vorbis audio',
  			'kindAudioWAV'    : 'WAV audio',
  			'AudioPlaylist'   : 'MP3 playlist',
  			'kindVideo'       : 'Video media',
  			'kindVideoDV'     : 'DV movie',
  			'kindVideoMPEG'   : 'MPEG movie',
  			'kindVideoMPEG4'  : 'MPEG-4 movie',
  			'kindVideoAVI'    : 'AVI movie',
  			'kindVideoMOV'    : 'Quick Time movie',
  			'kindVideoWM'     : 'Windows Media movie',
  			'kindVideoFlash'  : 'Flash movie',
  			'kindVideoMKV'    : 'Matroska movie',
  			'kindVideoOGG'    : 'Ogg movie'
  		}
  	};
  }
  
  
  
  /*
   * File: /js/ui/button.js
   */
  
  /**
   * @class  elFinder toolbar button widget.
   * If command has variants - create menu
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderbutton = function(cmd) {
  	return this.each(function() {
  		
  		var c        = 'class',
  			fm       = cmd.fm,
  			disabled = fm.res(c, 'disabled'),
  			active   = fm.res(c, 'active'),
  			hover    = fm.res(c, 'hover'),
  			item     = 'elfinder-button-menu-item',
  			selected = 'elfinder-button-menu-item-selected',
  			menu,
  			button   = $(this).addClass('ui-state-default elfinder-button')
  				.attr('title', cmd.title)
  				.append('<span class="elfinder-button-icon elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name) + '"/>')
  				.hover(function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover) /**button.toggleClass(hover);*/ })
  				.click(function(e) { 
  					if (!button.hasClass(disabled)) {
  						if (menu && cmd.variants.length > 1) {
  							// close other menus
  							menu.is(':hidden') && cmd.fm.getUI().click();
  							e.stopPropagation();
  							menu.slideToggle(100);
  						} else {
  							cmd.exec();
  						}
  						
  					}
  				}),
  			hideMenu = function() {
  				menu.hide();
  			};
  			
  		// if command has variants create menu
  		if ($.isArray(cmd.variants)) {
  			button.addClass('elfinder-menubutton');
  			
  			menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  				.hide()
  				.appendTo(button)
  				.on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover) })
  				.on('click', '.'+item, function(e) {
  					e.preventDefault();
  					e.stopPropagation();
  					button.removeClass(hover);
  					menu.hide();
  					cmd.exec(cmd.fm.selected(), $(this).data('value'));
  				});
  
  			cmd.fm.bind('disable select', hideMenu).getUI().click(hideMenu);
  			
  			cmd.change(function() {
  				menu.html('');
  				$.each(cmd.variants, function(i, variant) {
  					menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : ''));
  				});
  			});
  		}	
  			
  		cmd.change(function() {
  			if (cmd.disabled()) {
  				button.removeClass(active+' '+hover).addClass(disabled);
  			} else {
  				button.removeClass(disabled);
  				button[cmd.active() ? 'addClass' : 'removeClass'](active);
  			}
  		})
  		.change();
  	});
  };
  
  
  /*
   * File: /js/ui/contextmenu.js
   */
  
  /**
   * @class  elFinder contextmenu
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindercontextmenu = function(fm) {
  	
  	return this.each(function() {
  		var self   = $(this),
  			cmItem = 'elfinder-contextmenu-item',
  			smItem = 'elfinder-contextsubmenu-item',
  			exIcon = 'elfinder-contextmenu-extra-icon',
  			menu = self.addClass('ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction)
  				.hide()
  				.on('mouseenter mouseleave', '.'+cmItem, function(e) {
  					$(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  				})
  				.on('mouseenter mouseleave', '.'+exIcon, function(e) {
  					$(this).parent().toggleClass('ui-state-hover', e.type === 'mouseleave');
  				})
  				.on('contextmenu', function(){return false;}),
  			subpos  = fm.direction == 'ltr' ? 'left' : 'right',
  			types = $.extend({}, fm.options.contextmenu),
  			tpl     = '<div class="'+cmItem+'"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"/><span>{label}</span></div>',
  			item = function(label, icon, callback) {
  				return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : '').replace('{label}', label))
  					.click(function(e) {
  						e.stopPropagation();
  						e.preventDefault();
  						callback();
  					});
  			},
  			base, cwd,
  
  			autoToggle = function() {
  				var evTouchStart = 'touchstart.contextmenuAutoToggle';
  				menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  				if (menu.is(':visible')) {
  					menu.on('touchstart', function(e) {
  						if (e.originalEvent.touches.length > 1) {
  							return;
  						}
  						menu.stop().show();
  						menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  					})
  					.data('hideTm', setTimeout(function() {
  						cwd.find('.elfinder-cwd-file').off(evTouchStart);
  						cwd.find('.elfinder-cwd-file.ui-selected')
  						.one(evTouchStart, function(e) {
  							if (e.originalEvent.touches.length > 1) {
  								return;
  							}
  							var tgt = $(e.target);
  							if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) {
  								open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
  								return false;
  							}
  							cwd.find('.elfinder-cwd-file').off(evTouchStart);
  						})
  						.one('unselect.'+fm.namespace, function() {
  							cwd.find('.elfinder-cwd-file').off(evTouchStart);
  						});
  						menu.fadeOut({
  							duration: 300,
  							fail: function() {
  								menu.css('opacity', '1').show();
  							}
  						});
  					}, 4500));
  				}
  			},
  			
  			open = function(x, y) {
  				var width      = menu.outerWidth(),
  					height     = menu.outerHeight(),
  					bpos       = base.offset(),
  					bwidth     = base.width(),
  					bheight    = base.height(),
  					mw         = fm.UA.Mobile? 40 : 2,
  					mh         = fm.UA.Mobile? 20 : 2,
  					body       = $('body'),
  					x          = x - (bpos? bpos.left : 0),
  					y          = y - (bpos? bpos.top : 0),
  					css        = {
  						top  : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)),
  						left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width),
  						opacity : '1'
  					};
  
  				menu.stop().css(css).show();
  				
  				css[subpos] = parseInt(menu.width());
  				menu.find('.elfinder-contextmenu-sub').css(css);
  				if (fm.UA.iOS) {
  					$('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto');
  				}
  				fm.UA.Mobile && autoToggle();
  			},
  			
  			close = function() {
  				menu.hide().empty();
  				fm.trigger('closecontextmenu');
  				if (fm.UA.iOS) {
  					$('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch');
  				}
  			},
  			
  			create = function(type, targets) {
  				var sep = false,
  				cmdMap = {}, disabled = [], isCwd = (targets[0].indexOf(fm.cwd().volumeid, 0) === 0),
  				selcnt = 0;
  
  				if (self.data('cmdMaps')) {
  					$.each(self.data('cmdMaps'), function(i, v){
  						if (targets[0].indexOf(i, 0) == 0) {
  							cmdMap = v;
  							return false;
  						}
  					});
  				}
  				if (!isCwd) {
  					if (fm.disabledCmds) {
  						$.each(fm.disabledCmds, function(i, v){
  							if (targets[0].indexOf(i, 0) == 0) {
  								disabled = v;
  								return false;
  							}
  						});
  					}
  				}
  				if (type === 'navbar') {
  					fm.select({selected: targets});
  				}
  
  				selcnt = fm.selected().length;
  				if (selcnt > 1) {
  					menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>'
  					 + fm.i18n('selectedItems', ''+selcnt)
  					 + '</span></div>');
  				}
  				$.each(types[type]||[], function(i, name) {
  					var cmd, node, submenu, hover;
  					
  					if (name == '|' && sep) {
  						menu.append('<div class="elfinder-contextmenu-separator"/>');
  						sep = false;
  						return;
  					}
  					
  					if (cmdMap[name]) {
  						name = cmdMap[name];
  					}
  					cmd = fm.command(name);
  
  					if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) {
  						cmd.__disabled = cmd._disabled;
  						cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[name] ? $.inArray(name, disabled) === -1 : false));
  						$.each(cmd.linkedCmds, function(i, n) {
  							var c;
  							if (c = fm.command(n)) {
  								c.__disabled = c._disabled;
  								c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? $.inArray(n, disabled) === -1 : false));
  							}
  						});
  					}
  
  					if (cmd && cmd.getstate(targets) != -1) {
  						if (cmd.variants) {
  							if (!cmd.variants.length) {
  								return;
  							}
  							node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){})
  							.on('touchend', function(e){
  								node.data('touching', true);
  								setTimeout(function(){node.data('touching', false);}, 50);
  							})
  							.on('click touchend', '.'+smItem, function(e){
  								var opts;
  								e.stopPropagation();
  								if (node.data('touching')) {
  									node.data('touching', false);
  									$(this).removeClass('ui-state-hover');
  									e.preventDefault();
  								} else if (e.type == 'click') {
  									menu.hide();
  									opts = $(this).data('exec');
  									if ($.isPlainObject(opts)) {
  										opts._currentType = type;
  									}
  									cmd.exec(targets, opts);
  								}
  							});
  							
  							submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>')
  								.appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>'));
  							
  							hover = function(show){
  								if (! show) {
  									submenu.hide();
  								} else {
  									submenu.css({ left: 'auto', right: 'auto' });
  									var nodeOffset = node.offset(),
  										baseleft   = nodeOffset.left,
  										basetop    = nodeOffset.top,
  										basewidth  = node.outerWidth(),
  										width      = submenu.outerWidth(),
  										height     = submenu.outerHeight(),
  										baseOffset = base.offset(),
  										wwidth     = baseOffset.left + base.width(),
  										wheight    = baseOffset.top + base.height(),
  										x, y, over;
  	
  									over = (baseleft + basewidth + width) - wwidth;
  									x = (baseleft > width && over > 0)? (fm.UA.Mobile? 10 - width : basewidth - over) : basewidth;
  									if (subpos === 'right' && baseleft < width) {
  										x = fm.UA.Mobile? 30 - basewidth : basewidth - (width - baseleft);
  									}
  									over = (basetop + 5 + height) - wheight;
  									y = (over > 0 && basetop < wheight)? 10 - over : (over > 0? 30 - height : 5);
  	
  									submenu.css({ top : y }).css(subpos, x).show();
  								}
  							};
  							
  							node.addClass('elfinder-contextmenu-group').hover(function(e){
  								if (fm.UA.Mobile) {
  									hover(e.type === 'mouseenter');
  								} else {
  									if (e.type === 'mouseleave') {
  										node.data('timer', setTimeout(function() {
  											node.data('timer', null);
  											hover(false);
  										}, 250));
  									} else {
  										if (node.data('timer')) {
  											clearTimeout(node.data('timer'));
  											node.data('timer', null);
  										}
  										hover(true);
  									}
  								}
  							});
  							
  							$.each(cmd.variants, function(i, variant) {
  								submenu.append(
  									variant === '|' ? '<div class="elfinder-contextmenu-separator"/>' :
  									$('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0])
  								);
  							});
  								
  						} else {
  							node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() {
  								close();
  								cmd.exec(targets, {_currentType: type});
  							});
  							if (cmd.extra && cmd.extra.node) {
  								$('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' elfinder-contextmenu-extra-icon"/>')
  									.append(cmd.extra.node).appendTo(node);
  								$(cmd.extra.node).trigger('ready');
  							} else {
  								node.remove('.elfinder-contextmenu-extra-icon');
  							}
  						}
  						
  						menu.append(node)
  						sep = true;
  					}
  					
  					if (cmd && typeof cmd.__disabled !== 'undefined') {
  						cmd._disabled = cmd.__disabled;
  						delete cmd.__disabled;
  						$.each(cmd.linkedCmds, function(i, n) {
  							var c;
  							if (c = fm.command(n)) {
  								c._disabled = c.__disabled;
  								delete c.__disabled;
  							}
  						});
  					}
  				});
  			},
  			
  			createFromRaw = function(raw) {
  				$.each(raw, function(i, data) {
  					var node;
  					
  					if (data === '|') {
  						menu.append('<div class="elfinder-contextmenu-separator"/>');
  					} else if (data.label && typeof data.callback == 'function') {
  						node = item(data.label, data.icon, function() {
  							!data.remain && close();
  							data.callback();
  						});
  						menu.append(node);
  					}
  				});
  			};
  		
  		fm.one('load', function() {
  			base = fm.getUI();
  			cwd = fm.getUI('cwd');
  			fm.bind('contextmenu', function(e) {
  				var data = e.data;
  
  				if (!data.type || data.type !== 'files') {
  					cwd.trigger('unselectall');
  				}
  				close();
  
  				if (data.type && data.targets) {
  					create(data.type, data.targets);
  				} else if (data.raw) {
  					createFromRaw(data.raw);
  				}
  
  				menu.children().length && open(data.x, data.y);
  			})
  			.one('destroy', function() { menu.remove(); })
  			.bind('disable select', function(){
  				// 'mouseEvInternal' for Firefox's bug (maybe)
  				!self.data('mouseEvInternal') && close();
  				self.data('mouseEvInternal', false);
  			})
  			.getUI().click(close);
  		});
  		
  	});
  	
  };
  
  
  /*
   * File: /js/ui/cwd.js
   */
  
  /**
   * elFinder current working directory ui.
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindercwd = function(fm, options) {
  	
  	this.not('.elfinder-cwd').each(function() {
  		// fm.time('cwdLoad');
  		
  		var mobile = fm.UA.Mobile,
  			list = fm.viewType == 'list',
  
  			undef = 'undefined',
  			/**
  			 * Select event full name
  			 *
  			 * @type String
  			 **/
  			evtSelect = 'select.'+fm.namespace,
  			
  			/**
  			 * Unselect event full name
  			 *
  			 * @type String
  			 **/
  			evtUnselect = 'unselect.'+fm.namespace,
  			
  			/**
  			 * Disable event full name
  			 *
  			 * @type String
  			 **/
  			evtDisable = 'disable.'+fm.namespace,
  			
  			/**
  			 * Disable event full name
  			 *
  			 * @type String
  			 **/
  			evtEnable = 'enable.'+fm.namespace,
  			
  			c = 'class',
  			/**
  			 * File css class
  			 *
  			 * @type String
  			 **/
  			clFile       = fm.res(c, 'cwdfile'),
  			
  			/**
  			 * Selected css class
  			 *
  			 * @type String
  			 **/
  			fileSelector = '.'+clFile,
  			
  			/**
  			 * Selected css class
  			 *
  			 * @type String
  			 **/
  			clSelected = 'ui-selected',
  			
  			/**
  			 * Disabled css class
  			 *
  			 * @type String
  			 **/
  			clDisabled = fm.res(c, 'disabled'),
  			
  			/**
  			 * Draggable css class
  			 *
  			 * @type String
  			 **/
  			clDraggable = fm.res(c, 'draggable'),
  			
  			/**
  			 * Droppable css class
  			 *
  			 * @type String
  			 **/
  			clDroppable = fm.res(c, 'droppable'),
  			
  			/**
  			 * Hover css class
  			 *
  			 * @type String
  			 **/
  			clHover     = fm.res(c, 'hover'), 
  
  			/**
  			 * Hover css class
  			 *
  			 * @type String
  			 **/
  			clDropActive = fm.res(c, 'adroppable'),
  
  			/**
  			 * Css class for temporary nodes (for mkdir/mkfile) commands
  			 *
  			 * @type String
  			 **/
  			clTmp = clFile+'-tmp',
  
  			/**
  			 * Number of thumbnails to load in one request (new api only)
  			 *
  			 * @type Number
  			 **/
  			tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
  			
  			/**
  			 * Current search query.
  			 *
  			 * @type String
  			 */
  			query = '',
  
  			/**
  			 * Currect clipboard(cut) hashes as object key
  			 * 
  			 * @type Object
  			 */
  			clipCuts = {},
  
  			/**
  			 * Parents hashes of cwd
  			 *
  			 * @type Array
  			 */
  			cwdParents = [],
  			
  			/**
  			 * cwd current hashes
  			 * 
  			 * @type Array
  			 */
  			cwdHashes = [],
  
  			/**
  			 * incsearch current hashes
  			 * 
  			 * @type Array
  			 */
  			incHashes = void 0,
  
  			/**
  			 * Custom columns name and order
  			 *
  			 * @type Array
  			 */
  			customCols = [],
  
  			/**
  			 * Custom columns builder
  			 *
  			 * @type Function
  			 */
  			customColsBuild = function() {
  				var cols = '';
  				for (var i = 0; i < customCols.length; i++) {
  					cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
  				}
  				return cols;
  			},
  
  			/**
  			 * Make template.row from customCols
  			 *
  			 * @type Function
  			 */
  			makeTemplateRow = function() {
  				return '<tr id="{id}" class="'+clFile+' {permsclass} {dirclass}" title="{tooltip}"{css}><td class="elfinder-col-name"><div class="elfinder-cwd-file-wrapper"><span class="elfinder-cwd-icon {mime}"{style}/>{marker}<span class="elfinder-cwd-filename">{name}</span></div>'+selectCheckbox+'</td>'+customColsBuild()+'</tr>';
  			},
  			
  			selectCheckbox = ($.map(options.showSelectCheckboxUA, function(t) {return (fm.UA[t] || t.match(/^all$/i))? true : null;}).length)? '<div class="elfinder-cwd-select"><input type="checkbox"></div>' : '',
  
  			colResizing = false,
  			
  			colWidth = null,
  
  			/**
  			 * File templates
  			 *
  			 * @type Object
  			 **/
  			templates = {
  				icon : '<div id="{id}" class="'+clFile+' {permsclass} {dirclass} ui-corner-all" title="{tooltip}"><div class="elfinder-cwd-file-wrapper ui-corner-all"><div class="elfinder-cwd-icon {mime} ui-corner-all" unselectable="on"{style}/>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div>'+selectCheckbox+'</div>',
  				row  : '',
  			},
  			
  			permsTpl = fm.res('tpl', 'perms'),
  			
  			lockTpl = fm.res('tpl', 'lock'),
  			
  			symlinkTpl = fm.res('tpl', 'symlink'),
  			
  			/**
  			 * Template placeholders replacement rules
  			 *
  			 * @type Object
  			 **/
  			replacement = {
  				id : function(f) {
  					return fm.cwdHash2Id(f.hash);
  				},
  				name : function(f) {
  					var name = fm.escape(f.name);
  					!list && (name = name.replace(/([_.])/g, '&#8203;$1'));
  					return name;
  				},
  				nametitle : function(f) {
  					return fm.escape(f.name);
  				},
  				permsclass : function(f) {
  					return fm.perms2class(f);
  				},
  				perm : function(f) {
  					return fm.formatPermissions(f);
  				},
  				dirclass : function(f) {
  					var cName = f.mime == 'directory' ? 'directory' : '';
  					options.getClass && (cName += ' ' + options.getClass(f));
  					return cName;
  				},
  				style : function(f) {
  					return f.icon? 'style="background:url(\''+fm.escape(f.icon)+'\') 0 0 no-repeat;background-size:contain;"' : '';
  				},
  				mime : function(f) {
  					return fm.mime2class(f.mime);
  				},
  				size : function(f) {
  					return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
  				},
  				date : function(f) {
  					return fm.formatDate(f);
  				},
  				kind : function(f) {
  					return fm.mime2kind(f);
  				},
  				mode : function(f) {
  					return f.perm? fm.formatFileMode(f.perm) : '';
  				},
  				modestr : function(f) {
  					return f.perm? fm.formatFileMode(f.perm, 'string') : '';
  				},
  				modeoct : function(f) {
  					return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
  				},
  				modeboth : function(f) {
  					return f.perm? fm.formatFileMode(f.perm, 'both') : '';
  				},
  				marker : function(f) {
  					return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
  				},
  				tooltip : function(f) {
  					var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  						info  = '';
  					if (query && f.path) {
  						info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  					} else {
  						info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  					}
  					if (list) {
  						info += (info? '&#13;' : '') + fm.escape(f.name);
  					}
  					return info? info + '&#13;' + title : title;
  				}
  			},
  			
  			/**
  			 * Return file html
  			 *
  			 * @param  Object  file info
  			 * @return String
  			 **/
  			itemhtml = function(f) {
  				return templates[list ? 'row' : 'icon']
  						.replace(/\{([a-z]+)\}/g, function(s, e) { 
  							return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : ''); 
  						});
  			},
  			
  			/**
  			 * Flag. Required for msie to avoid unselect files on dragstart
  			 *
  			 * @type Boolean
  			 **/
  			selectLock = false,
  			
  			/**
  			 * Move selection to prev/next file
  			 *
  			 * @param String  move direction
  			 * @param Boolean append to current selection
  			 * @return void
  			 * @rise select			
  			 */
  			select = function(keyCode, append) {
  				var code     = $.ui.keyCode,
  					prev     = keyCode == code.LEFT || keyCode == code.UP,
  					sel      = cwd.find('[id].'+clSelected),
  					selector = prev ? 'first:' : 'last',
  					s, n, sib, top, left;
  
  				function sibling(n, direction) {
  					return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
  				}
  				
  				if (sel.length) {
  					s = sel.filter(prev ? ':first' : ':last');
  					sib = sibling(s, prev ? 'prev' : 'next');
  					
  					if (!sib.length) {
  						// there is no sibling on required side - do not move selection
  						n = s;
  					} else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
  						// find real prevoius file
  						n = sib;
  					} else {
  						// find up/down side file in icons view
  						top = s.position().top;
  						left = s.position().left;
  
  						n = s;
  						if (prev) {
  							do {
  								n = n.prev('[id]');
  							} while (n.length && !(n.position().top < top && n.position().left <= left));
  
  							if (n.hasClass(clDisabled)) {
  								n = sibling(n, 'next');
  							}
  						} else {
  							do {
  								n = n.next('[id]');
  							} while (n.length && !(n.position().top > top && n.position().left >= left));
  							
  							if (n.hasClass(clDisabled)) {
  								n = sibling(n, 'prev');
  							}
  							// there is row before last one - select last file
  							if (!n.length) {
  								sib = cwd.find('[id]:not(.'+clDisabled+'):last');
  								if (sib.position().top > top) {
  									n = sib;
  								}
  							}
  						}
  					}
  					// !append && unselectAll();
  				} else {
  					// there are no selected file - select first/last one
  					n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first'));
  				}
  				
  				if (n && n.length && !n.hasClass('elfinder-cwd-parent')) {
  					if (append) {
  						// append new files to selected
  						n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
  					} else {
  						// unselect selected files
  						sel.trigger(evtUnselect);
  					}
  					// select file(s)
  					n.trigger(evtSelect);
  					// set its visible
  					scrollToView(n.filter(prev ? ':first' : ':last'));
  					// update cache/view
  					trigger();
  				}
  			},
  			
  			selectedFiles = [],
  			
  			selectFile = function(hash) {
  				$('#'+fm.cwdHash2Id(hash)).trigger(evtSelect);
  			},
  			
  			selectAll = function() {
  				var phash = fm.cwd().hash;
  
  				selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
  				fm.lazy(function() {
  					cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
  					selectedFiles = (incHashes || cwdHashes).concat();
  					trigger();
  					selectCheckbox && selectAllCheckbox.data('pending', false);
  					cwd.addClass('elfinder-cwd-allselected');
  				});
  			},
  			
  			/**
  			 * Unselect all files
  			 *
  			 * @return void
  			 */
  			unselectAll = function() {
  				selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  				if (selectedFiles.length) {
  					selectLock = false;
  					selectedFiles = [];
  					cwd.find('[id].'+clSelected).trigger(evtUnselect);
  					selectCheckbox && cwd.find('input:checkbox').prop('checked', false);
  				} else {
  					fm.select({selected: []});
  				}
  				trigger();
  				selectCheckbox && selectAllCheckbox.data('pending', false);
  				cwd.removeClass('elfinder-cwd-allselected');
  			},
  			
  			/**
  			 * Return selected files hashes list
  			 *
  			 * @return Array
  			 */
  			selected = function() {
  				return selectedFiles;
  			},
  			
  			/**
  			 * Fire elfinder "select" event and pass selected files to it
  			 *
  			 * @return void
  			 */
  			trigger = function() {
  				if (selectCheckbox) {
  					var all = (selectedFiles.length === cwdHashes.length);
  					selectAllCheckbox.find('input').prop('checked', all);
  					cwd[all? 'addClass' : 'removeClass']('elfinder-cwd-allselected');
  				}
  				fm.trigger('select', {selected : selectedFiles});
  			},
  			
  			/**
  			 * Scroll file to set it visible
  			 *
  			 * @param DOMElement  file/dir node
  			 * @return void
  			 */
  			scrollToView = function(o) {
  				var ftop    = o.position().top,
  					fheight = o.outerHeight(true),
  					wtop    = wrapper.scrollTop(),
  					wheight = wrapper.get(0).clientHeight,
  					thheight = tableHeader? tableHeader.outerHeight(true) : 0;
  
  				if (ftop + thheight + fheight > wtop + wheight) {
  					wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight));
  				} else if (ftop < wtop) {
  					wrapper.scrollTop(ftop);
  				}
  				list && wrapper.scrollLeft(0);
  			},
  			
  			/**
  			 * Files we get from server but not show yet
  			 *
  			 * @type Array
  			 **/
  			buffer = [],
  			
  			/**
  			 * Extra data of buffer
  			 *
  			 * @type Object
  			 **/
  			bufferExt = {},
  			
  			/**
  			 * Return index of elements with required hash in buffer 
  			 *
  			 * @param String  file hash
  			 * @return Number
  			 */
  			index = function(hash) {
  				var l = buffer.length;
  				
  				while (l--) {
  					if (buffer[l].hash == hash) {
  						return l;
  					}
  				}
  				return -1;
  			},
  			
  			/**
  			 * Scroll event name
  			 *
  			 * @type String
  			 **/
  			scrollEvent = 'elfscrstop',
  			
  			/**
  			 * jQuery UI selectable option
  			 * 
  			 * @type Object
  			 */
  			selectableOption = {
  				filter     : fileSelector,
  				stop       : trigger,
  				delay      : 250,
  				selected   : function(e, ui) { $(ui.selected).trigger(evtSelect); },
  				unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); }
  			},
  			
  			/**
  			 * display parent folder with ".." name
  			 * 
  			 * @param  String  phash
  			 * @return void
  			 */
  			oldSchool = function(phash) {
  				var phash = fm.cwd().phash,
  					pdir  = fm.file(phash) || null,
  					set   = function(pdir) {
  						if (pdir) {
  							parent = $(itemhtml($.extend(true, {}, pdir, {name : '..', mime : 'directory'})))
  								.addClass('elfinder-cwd-parent')
  								.bind('mousedown click mouseup touchstart touchmove touchend dblclick mouseenter', function(e) {
  									e.preventDefault();
  									e.stopPropagation();
  								})
  								.dblclick(function() {
  									fm.exec('open', fm.cwdId2Hash(this.id));
  								}
  							);
  							(list ? cwd.find('tbody') : cwd).prepend(parent);
  						}
  					};
  				if (pdir) {
  					set(pdir);
  				} else {
  					if (fm.getUI('tree').hasClass('elfinder-tree')) {
  						fm.one('parents', function() {
  							set(fm.file(phash) || null);
  						});
  					} else {
  						fm.request({
  							data : {cmd : 'parents', target : fm.cwd().hash},
  							preventFail : true
  						})
  						.done(function(data) {
  							set(fm.file(phash) || null);
  						});
  					}
  				}
  			},
  			
  			/**
  			 * Cwd scroll event handler.
  			 * Lazy load - append to cwd not shown files
  			 *
  			 * @return void
  			 */
  			render = function() {
  				var place = (list ? cwd.children('table').children('tbody') : cwd),
  					chk,
  					phash,
  					proc  = false,
  					go    = function(over){
  						var over  = over || null,
  							html  = [],
  							dirs  = false,
  							atmb  = {},
  							stmb  = (fm.option('tmbUrl') === 'self'),
  							files, locks, $place, selected;
  						
  						files = buffer.splice(0, fm.options.showFiles + (over || 0) / (bufferExt.hpi || 1));
  						bufferExt.renderd += files.length;
  						if (! buffer.length) {
  							bottomMarker.hide();
  							wrapper.off(scrollEvent, render);
  						}
  						
  						locks = [];
  						html = $.map(files, function(f) {
  							if (f.hash && f.name) {
  								if (f.mime == 'directory') {
  									dirs = true;
  								}
  								if (f.tmb || (stmb && f.mime.indexOf('image/') === 0)) {
  									atmb[f.hash] = f.tmb;
  								}
  								clipCuts[f.hash] && locks.push(f.hash);
  								return itemhtml(f);
  							}
  							return null;
  						});
  
  						// html into created document fragment
  						$place = $(document.createDocumentFragment()).append(html.join(''));
  						
  						// make directory droppable
  						dirs && !mobile && makeDroppable($place);
  						
  						// check selected items
  						selected = [];
  						if (selectedFiles.length) {
  							$place.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() {
  								$.inArray(fm.cwdId2Hash(this.id), selectedFiles) !== -1 && selected.push($(this));
  							});
  						}
  						
  						// append to cwd
  						place.append($place);
  						
  						// trigger select
  						selected.length && $.each(selected, function(i, n) { n.trigger(evtSelect); });
  						
  						locks.length && fm.trigger('lockfiles', {files: locks});
  						!bufferExt.hpi && bottomMarkerShow(place, files.length);
  						
  						if (list) {
  							// show thead
  							cwd.find('thead').show();
  							// fixed table header
  							fixTableHeader({fitWidth: ! colWidth});
  						}
  						
  						if (Object.keys(atmb).length) {
  							if (Object.keys(bufferExt.attachTmbs).length < 1) {
  								wrapper.off(scrollEvent, attachThumbnails).on(scrollEvent, attachThumbnails);
  								fm.unbind('resize', attachThumbnails).bind('resize', attachThumbnails);
  							}
  							$.extend(bufferExt.attachTmbs, atmb);
  							attachThumbnails();
  						}
  					};
  				
  				if (! bufferExt.renderd) {
  					// first time to go()
  					proc = true;
  					// scroll top on dir load to avoid scroll after page reload
  					wrapper.scrollTop(0);
  					phash = fm.cwd().phash;
  					go();
  					if (options.oldSchool && phash && !query) {
  						oldSchool(phash);
  					}
  					if (list) {
  						colWidth && setColwidth();
  						fixTableHeader({fitWidth: true});
  					}
  					fm.trigger('cwdrender');
  					proc = false;
  				} else if (! proc) {
  					// next go()
  					if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) {
  						proc = true;
  						fm.lazy(function() {
  							go(chk);
  							proc = false;;
  						});
  					}
  				}
  			},
  			
  			// fixed table header jQuery object
  			tableHeader = null,
  			
  			// To fixed table header colmun
  			fixTableHeader = function(opts) {
  				if (! options.listView.fixedHeader) {
  					return;
  				}
  				var setPos = function() {
  					var val, pos;
  					
  					if (fm.direction === 'ltr') {
  						val = wrapper.scrollLeft() * -1;
  						pos = 'left';
  					} else {
  						val = wrapper.scrollLeft();
  						pos = 'right';
  					}
  					if (base.css(pos) !== val) {
  						base.css(pos, val);
  					}
  				},
  				opts = opts || {},
  				cnt, base, table, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init;
  				
  				tbody = cwd.find('tbody');
  				btr = tbody.children('tr:first');
  				if (btr.length) {
  					table = tbody.parent();
  					if (! tableHeader) {
  						init = true;
  						tbody.addClass('elfinder-cwd-fixheader');
  						thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead');
  						htr = thead.children('tr:first');
  						hheight = htr.outerHeight(true);
  						cwd.css('margin-top', hheight - parseInt(table.css('padding-top')));
  						base = $('<div/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  						tableHeader = $('<div/>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader')
  							.removeClass('ui-droppable native-droppable')
  							.css(wrapper.position())
  							.css('height', hheight)
  							.append(base);
  						if (fm.direction === 'rtl') {
  							tableHeader.css('right', (fm.getUI().width() - wrapper.width()) + 'px');
  						}
  						setPos();
  						wrapper.after(tableHeader)
  							.on('scroll.fixheader resize.fixheader', function(e) {
  								setPos();
  								if (e.type === 'resize') {
  									e.stopPropagation();
  									fixTableHeader();
  								}
  							});
  					} else {
  						thead = $('#'+fm.namespace+'-cwd-thead');
  						htr = thead.children('tr:first');
  					}
  					
  					if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) {
  						cnt = customCols.length + 1;
  						for (var i = 0; i < cnt; i++) {
  							htd = htr.children('td:eq('+i+')');
  							btd = btr.children('td:eq('+i+')');
  							htw = htd.width();
  							btw = btd.width();
  							if (typeof htd.data('delta') === 'undefined') {
  								htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw));
  							}
  							btw -= htd.data('delta');
  							if (htw === btw) {
  								break;
  							}
  							htd.css('width', btw + 'px');
  						}
  					}
  					
  					tableHeader.data('widthTimer') && clearTimeout(tableHeader.data('widthTimer'));
  					tableHeader.data('widthTimer', setTimeout(function() {
  						if (tableHeader) {
  							if (fm.direction === 'rtl') {
  								tableHeader.css('right', (fm.getUI().width() - wrapper.width()) + 'px');
  							}
  							tableHeader.css(wrapper.position()).css('width', cwd.outerWidth() + 'px');
  						}
  					}, 10));
  				}
  			},
  			
  			// Set colmun width
  			setColwidth = function() {
  				if (list && colWidth) {
  					var cl = 'elfinder-cwd-colwidth',
  					first = cwd.find('tr[id]:first'),
  					former;
  					if (! first.hasClass(cl)) {
  						former = cwd.find('tr.'+cl);
  						former.removeClass(cl).find('td').css('width', '');
  						first.addClass(cl);
  						cwd.find('table:first').css('table-layout', 'fixed');
  						$.each($.merge(['name'], customCols), function(i, k) {
  							var w = colWidth[k] || first.find('td.elfinder-col-'+k).width();
  							first.find('td.elfinder-col-'+k).width(w);
  						});
  					}
  				}
  			},
  			
  			/**
  			 * Droppable options for cwd.
  			 * Drop target is `wrapper`
  			 * Do not add class on childs file over
  			 *
  			 * @type Object
  			 */
  			droppable = $.extend({}, fm.droppable, {
  				over : function(e, ui) {
  					var dst    = $(this),
  						helper = ui.helper,
  						ctr    = (e.shiftKey || e.ctrlKey || e.metaKey),
  						hash, status, inParent;
  					e.stopPropagation();
  					helper.data('dropover', helper.data('dropover') + 1);
  					dst.data('dropover', true);
  					if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  						dst.removeClass(clDropActive);
  						helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  						return;
  					}
  					if (dst.hasClass(fm.res(c, 'cwdfile'))) {
  						hash = fm.cwdId2Hash(dst.attr('id'));
  						dst.data('dropover', hash);
  					} else {
  						hash = fm.cwd().hash;
  						fm.cwd().write && dst.data('dropover', hash);
  					}
  					inParent = (fm.file(helper.data('files')[0]).phash === hash);
  					if (dst.data('dropover') === hash) {
  						$.each(helper.data('files'), function(i, h) {
  							if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) {
  								dst.removeClass(clDropActive);
  								return false; // break $.each
  							}
  						});
  					} else {
  						dst.removeClass(clDropActive);
  					}
  					if (helper.data('locked') || inParent) {
  						status = 'elfinder-drag-helper-plus';
  					} else {
  						status = 'elfinder-drag-helper-move';
  						if (ctr) {
  							status += ' elfinder-drag-helper-plus';
  						}
  					}
  					dst.hasClass(clDropActive) && helper.addClass(status);
  					setTimeout(function(){ dst.hasClass(clDropActive) && helper.addClass(status); }, 20);
  				},
  				out : function(e, ui) {
  					var helper = ui.helper;
  					e.stopPropagation();
  					helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  					$(this).removeData('dropover')
  					       .removeClass(clDropActive);
  				},
  				deactivate : function() {
  					$(this).removeData('dropover')
  					       .removeClass(clDropActive);
  				},
  				drop : function(e, ui) {
  					unselectAll();
  					fm.droppable.drop.call(this, e, ui);
  				}
  			}),
  			
  			/**
  			 * Make directory droppable
  			 *
  			 * @return void
  			 */
  			makeDroppable = function(place) {
  				var targets = (place || cwd).find('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)');
  				if (fm.isCommandEnabled('paste')) {
  					targets.droppable(droppable);
  				}
  				if (fm.isCommandEnabled('upload')) {
  					targets.addClass('native-droppable');
  				}
  			},
  			
  			/**
  			 * Preload required thumbnails and on load add css to files.
  			 * Return false if required file is not visible yet (in buffer) -
  			 * required for old api to stop loading thumbnails.
  			 *
  			 * @param  Object  file hash -> thumbnail map
  			 * @return void
  			 */
  			attachThumbnails = function(image) {
  				var url  = fm.option('tmbUrl'),
  					done = [],
  					chk  = function(hash, tmb) {
  						var node = $('#'+fm.cwdHash2Id(hash)),
  							file;
  	
  						if (node.length) {
  							if (fm.isInWindow(node, true)) {
  								if (tmb != '1') {
  									file = fm.file(hash);
  									if (file.tmb == '1') {
  										file.tmb = tmb;
  									}
  									(function(node, tmb) {
  										$('<img/>')
  											.on('load', function() { node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  											.attr('src', tmb.url);
  									})(node, fm.tmb(file));
  								} else {
  									bufferExt.getTmbs.push(hash);
  								}
  								done.push(hash);
  							}
  						}
  					};
  
  				if ($.isPlainObject(image) && Object.keys(image).length) {
  					$.extend(bufferExt.attachTmbs, image);
  					$.each(image, chk);
  				} else {
  					$.each(bufferExt.attachTmbs, chk);
  				}
  				
  				$.each(done, function(i, h) {
  					delete bufferExt.attachTmbs[h];
  				});
  				if (bufferExt.getTmbs.length) {
  					loadThumbnails();
  				}
  				if (Object.keys(bufferExt.attachTmbs).length < 1) {
  					wrapper.off(scrollEvent, attachThumbnails);
  					fm.unbind('resize', attachThumbnails);
  				}
  			},
  			
  			/**
  			 * Load thumbnails from backend.
  			 *
  			 * @param  Array|Boolean  files hashes list for new api | true for old api
  			 * @return void
  			 */
  			loadThumbnails = function() {
  				var tmbs = [];
  				
  				// do not parallel request
  				if (bufferExt.gettingTmb) {
  					return;
  				}
  				
  				bufferExt.gettingTmb = true;
  				
  				if (fm.oldAPI) {
  					fm.request({
  						data : {cmd : 'tmb', current : fm.cwd().hash},
  						preventFail : true
  					})
  					.done(function(data) {
  						bufferExt.gettingTmb = false;
  						if (data.images && Object.keys(data.images).length) {
  							attachThumbnails(data.images);
  						}
  						if (data.tmb) {
  							loadThumbnails();
  						}
  					})
  					.fail(function() {
  						bufferExt.gettingTmb = false;
  					});
  					return;
  				} 
  
  				tmbs = bufferExt.getTmbs.splice(0, tmbNum);
  				if (tmbs.length) {
  					if (fm.isInWindow($('#'+fm.cwdHash2Id(tmbs[0])), true) || fm.isInWindow($('#'+fm.cwdHash2Id(tmbs[tmbs.length-1])), true)) {
  						fm.request({
  							data : {cmd : 'tmb', targets : tmbs},
  							preventFail : true
  						})
  						.done(function(data) {
  							bufferExt.gettingTmb = false;
  							if (data.images && Object.keys(data.images).length) {
  								attachThumbnails(data.images);
  							}
  							if (bufferExt.getTmbs.length) {
  								loadThumbnails();
  							}
  						})
  						.fail(function() {
  							bufferExt.gettingTmb = false;
  						});
  					} else {
  						// out of window tmb get later
  						$.each(tmbs, function(i, h) {
  							bufferExt.attachTmbs[h] = '1';
  						});
  						bufferExt.gettingTmb = false;
  						attachThumbnails();
  					}
  				}
  			},
  			
  			/**
  			 * Add new files to cwd/buffer
  			 *
  			 * @param  Array  new files
  			 * @return void
  			 */
  			add = function(files) {
  				var place    = list ? cwd.find('tbody') : cwd,
  					l        = files.length, 
  					atmb     = {},
  					dirs     = false,
  					findNode = function(file) {
  						var pointer = cwd.find('[id]:first'), file2;
  
  						while (pointer.length) {
  							file2 = fm.file(fm.cwdId2Hash(pointer.attr('id')));
  							if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) {
  								return pointer;
  							}
  							pointer = pointer.next('[id]');
  						}
  					},
  					findIndex = function(file) {
  						var l = buffer.length, i;
  						
  						for (i =0; i < l; i++) {
  							if (fm.compare(file, buffer[i]) < 0) {
  								return i;
  							}
  						}
  						return l || -1;
  					},
  					file, hash, node, ndx;
  
  				l && wz.removeClass('elfinder-cwd-wrapper-empty');
  				
  				while (l--) {
  					file = files[l];
  					hash = file.hash;
  					
  					if ($('#'+fm.cwdHash2Id(hash)).length) {
  						continue;
  					}
  					
  					if ((node = findNode(file)) && node.length) {
  						node.before(itemhtml(file)); 
  					} else if ((ndx = findIndex(file)) >= 0) {
  						buffer.splice(ndx, 0, file);
  					} else {
  						place.append(itemhtml(file));
  					}
  					
  					if ($('#'+fm.cwdHash2Id(hash)).length) {
  						if (file.mime == 'directory') {
  							dirs = true;
  						} else if (file.tmb) {
  							atmb[hash] = file.tmb;
  						}
  					}
  				}
  
  				// clear incsearch view
  				if (incHashes) {
  					incHashes = void 0;
  					content();
  				}
  
  				setColwidth();
  				bottomMarkerShow(place);
  				if (Object.keys(atmb).length) {
  					if (Object.keys(bufferExt.attachTmbs).length < 1) {
  						wrapper.off(scrollEvent, attachThumbnails).on(scrollEvent, attachThumbnails);
  						fm.unbind('resize', attachThumbnails).bind('resize', attachThumbnails);
  					}
  					$.extend(bufferExt.attachTmbs, atmb);
  					attachThumbnails();
  				}
  				dirs && !mobile && makeDroppable();
  			},
  			
  			/**
  			 * Remove files from cwd/buffer
  			 *
  			 * @param  Array  files hashes
  			 * @return void
  			 */
  			remove = function(files) {
  				var l = files.length, hash, n, ndx;
  
  				// removed cwd
  				if (!fm.cwd().hash && fm.currentReqCmd !== 'open') {
  					$.each(cwdParents.reverse(), function(i, h) {
  						if (fm.files()[h]) {
  							fm.one(fm.currentReqCmd, function(e, fm) {
  								!fm.cwd().hash && fm.exec('open', h);
  							});
  							return false;
  						}
  					});
  					return;
  				}
  				
  				while (l--) {
  					hash = files[l];
  					if ((n = $('#'+fm.cwdHash2Id(hash))).length) {
  						try {
  							n.remove();
  						} catch(e) {
  							fm.debug('error', e);
  						}
  					} else if ((ndx = index(hash)) != -1) {
  						buffer.splice(ndx, 1);
  					}
  				}
  				
  				setColwidth();
  			},
  			
  			msg = {
  				name : fm.i18n('name'),
  				perm : fm.i18n('perms'),
  				date : fm.i18n('modify'),
  				size : fm.i18n('size'),
  				kind : fm.i18n('kind'),
  				modestr : fm.i18n('mode'),
  				modeoct : fm.i18n('mode'),
  				modeboth : fm.i18n('mode'),
  			},
  			
  			customColsNameBuild = function() {
  				var name = '',
  				customColsName = '',
  				names = $.extend({}, msg, options.listView.columnsCustomName);
  				for (var i = 0; i < customCols.length; i++) {
  					if (typeof names[customCols[i]] !== 'undefined') {
  						name = names[customCols[i]];
  					} else {
  						name = fm.i18n(customCols[i]);
  					}
  					customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>';
  				}
  				return customColsName;
  			},
  			
  			bottomMarkerShow = function(place, cnt) {
  				var ph, col = 1;
  				place = place || (list ? cwd.find('tbody') : cwd);
  
  				if (buffer.length > 0) {
  					place.css({height: 'auto'});
  					ph = place.height();
  					if (cnt) {
  						if (! list) {
  							col = Math.floor(place.width()/place.find('[id]:first').width());
  							cnt = Math.ceil(cnt/col) * col;
  						}
  						bufferExt.hpi = ph / cnt;
  						bufferExt.row = bufferExt.hpi * col;
  					}
  					bottomMarker.css({top: (bufferExt.hpi * buffer.length + ph) + 'px'}).show();
  					if (wrapper.height() > ph) {
  						// show more items
  						wrapper.trigger(scrollEvent);
  					}
  
  				}
  			},
  			
  			wrapperContextMenu = {
  				contextmenu : function(e) {
  					e.preventDefault();
  					fm.trigger('contextmenu', {
  						'type'    : 'cwd',
  						'targets' : [fm.cwd().hash],
  						'x'       : e.pageX,
  						'y'       : e.pageY
  					});
  				},
  				touchstart : function(e) {
  					if (e.originalEvent.touches.length > 1) {
  						return;
  					}
  					cwd.data('longtap', null);
  					wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  					if (e.target === this || e.target === cwd.get(0)) {
  						cwd.data('tmlongtap', setTimeout(function(){
  							//if (wrapper.data('touching')) {
  								// long tap
  								cwd.data('longtap', true);
  								fm.trigger('contextmenu', {
  									'type'    : 'cwd',
  									'targets' : [fm.cwd().hash],
  									'x'       : wrapper.data('touching').x,
  									'y'       : wrapper.data('touching').y
  								});
  							//}
  						}, 500));
  					}
  				},
  				touchend : function(e) {
  					if (e.type === 'touchmove') {
  						if (! wrapper.data('touching') ||
  								( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX)
  								+ Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) {
  							wrapper.data('touching', null);
  						}
  					}
  					clearTimeout(cwd.data('tmlongtap'));
  				},
  				click : function(e) {
  					if (cwd.data('longtap')) {
  						e.preventDefault();
  						e.stopPropagation();
  					}
  				}
  			},
  			
  			/**
  			 * Update directory content
  			 *
  			 * @return void
  			 */
  			content = function() {
  				var phash, emptyMethod, thtr;
  
  				wz.append(selectAllCheckbox);
  				
  				try {
  					// to avoid problem with draggable
  					cwd.empty();
  				} catch (e) {
  					cwd.html('');
  				}
  				
  				if (tableHeader) {
  					wrapper.off('scroll.fixheader resize.fixheader');
  					tableHeader.remove();
  					tableHeader = null;
  				}
  
  				cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list')
  					.addClass('elfinder-cwd-view-'+(list ? 'list' :'icons'))
  					.attr('style', '')
  					.css('height', 'auto');
  				bottomMarker.hide();
  
  				wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')
  					._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom'));
  				if (fm.UA.iOS) {
  					wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  				}
  
  				if (list) {
  					cwd.html('<table><thead/><tbody/></table>');
  					thtr = $('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+msg.name+'</td>'+customColsNameBuild()+'</tr>');
  					cwd.find('thead').hide().append(
  						thtr
  						.on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  						.on('touchstart.'+fm.namespace, 'td', wrapperContextMenu.touchstart)
  						.on('touchmove.'+fm.namespace+' touchend.'+fm.namespace+' mouseup.'+fm.namespace, 'td', wrapperContextMenu.touchend)
  						.on('click.'+fm.namespace,'td', wrapperContextMenu.click)
  					).find('td:first').append(selectAllCheckbox);
  
  					if ($.fn.sortable) {
  						thtr.addClass('touch-punch touch-punch-keep-default')
  							.sortable({
  							axis: 'x',
  							distance: 8,
  							items: '> .sortable-item',
  							start: function(e, ui) {
  								$(ui.item[0]).data('dragging', true);
  								ui.placeholder
  									.width(ui.helper.removeClass('ui-state-hover').width())
  									.removeClass('ui-state-active')
  									.addClass('ui-state-hover')
  									.css('visibility', 'visible');
  							},
  							update: function(e, ui){
  								var target = $(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''),
  									prev, done;
  								customCols = $.map($(this).children(), function(n) {
  									var name = $(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '');
  									if (! done) {
  										if (target === name) {
  											done = true;
  										} else {
  											prev = name;
  										}
  									}
  									return (name === 'name')? null : name;
  								});
  								templates.row = makeTemplateRow();
  								fm.storage('cwdCols', customCols);
  								prev = '.elfinder-col-'+prev+':first';
  								target = '.elfinder-col-'+target+':first';
  								fm.lazy(function() {
  									cwd.find('tbody tr').each(function() {
  										var $this = $(this);
  										$this.children(prev).after($this.children(target));
  									});
  								});
  							},
  							stop: function(e, ui) {
  								setTimeout(function() {
  									$(ui.item[0]).removeData('dragging');
  								}, 100);
  							}
  						});
  					}
  
  					if ($.fn.resizable) {
  						thtr.find('td').addClass('touch-punch').resizable({
  							handles: fm.direction === 'ltr'? 'e' : 'w',
  							start: function(e, ui) {
  								var target = cwd.find('td.elfinder-col-'
  									+ ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '')
  									+ ':first');
  								
  								ui.element
  									.data('resizeTarget', target)
  									.data('targetWidth', target.width());
  								colResizing = true;
  								if (cwd.find('table').css('table-layout') !== 'fixed') {
  									cwd.find('tbody tr:first td').each(function() {
  										$(this).width($(this).width());
  									});
  									cwd.find('table').css('table-layout', 'fixed');
  								}
  							},
  							resize: function(e, ui) {
  								ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width));
  							},
  							stop : function() {
  								colResizing = false;
  								fixTableHeader({fitWidth: true});
  								colWidth = {};
  								cwd.find('tbody tr:first td').each(function() {
  									var name = $(this).attr('class').split(' ')[0].replace('elfinder-col-', '');
  									colWidth[name] = $(this).width();
  								});
  								fm.storage('cwdColWidth', colWidth);
  							}
  						})
  						.find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical');
  					}
  				}
  
  				fm.lazy(function() {
  					buffer = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null });
  					
  					buffer = fm.sortFiles(buffer);
  					bufferExt = {
  						renderd: 0,
  						attachTmbs: {},
  						getTmbs: []
  					};
  					
  					wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty');
  					wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent);
  					
  					// set droppable
  					if (!fm.cwd().write) {
  						wrapper.removeClass('native-droppable')
  						       .droppable('disable')
  						       .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974
  					} else {
  						wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable');
  						wrapper.droppable('enable');
  					}
  				});
  			},
  			
  			/**
  			 * CWD node itself
  			 *
  			 * @type JQuery
  			 **/
  			cwd = $(this)
  				.addClass('ui-helper-clearfix elfinder-cwd')
  				.attr('unselectable', 'on')
  				// fix ui.selectable bugs and add shift+click support 
  				.on('click.'+fm.namespace, fileSelector, function(e) {
  					var p    = this.id ? $(this) : $(this).parents('[id]:first'),
  						tgt  = $(e.target),
  						prev,
  						next,
  						pl,
  						nl,
  						sib;
  
  					if (selectCheckbox && (tgt.is('input:checkbox') || tgt.hasClass('elfinder-cwd-select'))) {
  						e.stopPropagation();
  						e.preventDefault();
  						if (! wrapper.data('touching')) {
  							p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  							trigger();
  						}
  						setTimeout(function() {
  							tgt.prop('checked', p.hasClass(clSelected));
  						}, 10);
  						
  						return false;
  					}
  					
  					if (cwd.data('longtap')) {
  						e.stopPropagation();
  						return;
  					}
  
  					if (e.shiftKey) {
  						prev = p.prevAll('.'+clSelected+':first');
  						next = p.nextAll('.'+clSelected+':first');
  						pl   = prev.length;
  						nl   = next.length;
  					}
  					if (e.shiftKey && (pl || nl)) {
  						sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id'));
  						sib.add(p).trigger(evtSelect);
  					} else if (e.ctrlKey || e.metaKey) {
  						p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  					} else {
  						if (wrapper.data('touching') && p.hasClass(clSelected)) {
  							wrapper.data('touching', null);
  							fm.dblclick({file : fm.cwdId2Hash(this.id)});
  							return;
  						} else {
  							unselectAll();
  							p.trigger(evtSelect);
  						}
  					}
  
  					trigger();
  				})
  				// call fm.open()
  				.on('dblclick.'+fm.namespace, fileSelector, function(e) {
  					fm.dblclick({file : fm.cwdId2Hash(this.id)});
  				})
  				// for touch device
  				.on('touchstart.'+fm.namespace, fileSelector, function(e) {
  					if (e.originalEvent.touches.length > 1) {
  						return;
  					}
  					var p   = this.id ? $(this) : $(this).parents('[id]:first'),
  						tgt = $(e.target),
  						sel;
  					
  					wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  					if (selectCheckbox && (tgt.is('input:checkbox') || tgt.hasClass('elfinder-cwd-select'))) {
  						setTimeout(function() {
  							if (wrapper.data('touching')) {
  								p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  								trigger();
  							}
  						}, 150);
  						return;
  					}
  
  					if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') {
  						return;
  					}
  					
  					sel = p.prevAll('.'+clSelected+':first').length +
  					      p.nextAll('.'+clSelected+':first').length;
  					cwd.data('longtap', null);
  					p.addClass(clHover)
  					 .data('tmlongtap', setTimeout(function(){
  						// long tap
  						cwd.data('longtap', true);
  						if (e.target.nodeName != 'TD' || fm.selected().length > 0) {
  							p.trigger(evtSelect);
  							trigger();
  							fm.trigger('contextmenu', {
  								'type'    : 'files',
  								'targets' : fm.selected(),
  								'x'       : e.originalEvent.touches[0].pageX,
  								'y'       : e.originalEvent.touches[0].pageY
  							});
  						}
  					}, 500));
  				})
  				.on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) {
  					if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA' || $(e.target).hasClass('elfinder-cwd-select')) {
  						return;
  					}
  					var p = this.id ? $(this) : $(this).parents('[id]:first');
  					clearTimeout(p.data('tmlongtap'));
  					if (e.type === 'touchmove') {
  						wrapper.data('touching', null);
  						p.removeClass(clHover);
  					} else if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) {
  						e.preventDefault();
  						wrapper.data('touching', null);
  						fm.dblclick({file : fm.cwdId2Hash(this.id)});
  					}
  				})
  				// attach draggable
  				.on('mouseenter.'+fm.namespace, fileSelector, function(e) {
  					var $this = $(this), helper = null,
  						target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  
  					if (!mobile && !$this.hasClass(clTmp) && !target.hasClass(clDraggable) && !target.hasClass(clDisabled)) {
  						target.on('mousedown', function(e) {
  							// shiftKey + drag start for HTML5 native drag function
  							if (e.shiftKey && !fm.UA.IE && cwd.data('selectable')) {
  								// destroy jQuery-ui selectable while trigger native drag
  								cwd.selectable('destroy').data('selectable', false);
  								setTimeout(function(){
  									cwd.selectable(selectableOption).data('selectable', true);
  								}, 10);
  							}
  							target.draggable('option', 'disabled', e.shiftKey);
  							if (e.shiftKey) {
  								target.attr('draggable', 'true');
  							} else {
  								target.attr('draggable', 'false')
  								      .draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47});
  							}
  						})
  						.on('dragstart', function(e) {
  							var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  							helper = null;
  							if (dt && !fm.UA.IE) {
  								var p = this.id ? $(this) : $(this).parents('[id]:first'),
  									elm   = $('<span>'),
  									url   = '',
  									durl  = null,
  									murl  = null,
  									files = [],
  									icon  = function(f) {
  										var mime = f.mime, i, tmb = fm.tmb(f);
  										i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>';
  										if (tmb) {
  											i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  										}
  										return i;
  									}, l, geturl = [];
  								p.trigger(evtSelect);
  								trigger();
  								$.each(selectedFiles, function(i, v){
  									var file = fm.file(v),
  										furl = file.url;
  									if (file && file.mime !== 'directory') {
  										if (!furl) {
  											furl = fm.url(file.hash);
  										} else if (furl == '1') {
  											geturl.push(v);
  											return true;
  										}
  										if (furl) {
  											furl = fm.convAbsUrl(furl);
  											files.push(v);
  											$('<a>').attr('href', furl).text(furl).appendTo(elm);
  											url += furl + "\n";
  											if (!durl) {
  												durl = file.mime + ':' + file.name + ':' + furl;
  											}
  											if (!murl) {
  												murl = furl + "\n" + file.name;
  											}
  										}
  									}
  								});
  								if (geturl.length) {
  									$.each(geturl, function(i, v){
  										var rfile = fm.file(v);
  										rfile.url = '';
  										fm.request({
  											data : {cmd : 'url', target : v},
  											notify : {type : 'url', cnt : 1},
  											preventDefault : true
  										})
  										.always(function(data) {
  											rfile.url = data.url? data.url : '1';
  										});
  									});
  									return false;
  								} else if (url) {
  									if (dt.setDragImage) {
  										helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body));
  										if ((l = files.length) > 1) {
  											helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>');
  										}
  										dt.setDragImage(helper.get(0), 50, 47);
  									}
  									dt.effectAllowed = 'copyLink';
  									dt.setData('DownloadURL', durl);
  									dt.setData('text/x-moz-url', murl);
  									dt.setData('text/uri-list', url);
  									dt.setData('text/plain', url);
  									dt.setData('text/html', elm.html());
  									dt.setData('elfinderfrom', window.location.href + fm.cwd().hash);
  									dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  								} else {
  									return false;
  								}
  							}
  						})
  						.on('dragend', function(e){
  							unselectAll();
  							helper && helper.remove();
  						})
  						.draggable(fm.draggable);
  					}
  				})
  				// add hover class to selected file
  				.on(evtSelect, fileSelector, function(e) {
  					var $this = $(this),
  						id    = fm.cwdId2Hash($this.attr('id'));
  					
  					if (!selectLock && !$this.hasClass(clDisabled)) {
  						$this.addClass(clSelected).children().addClass(clHover).find('input:checkbox').prop('checked', true);;
  						if ($.inArray(id, selectedFiles) === -1) {
  							selectedFiles.push(id);
  						}
  					}
  				})
  				// remove hover class from unselected file
  				.on(evtUnselect, fileSelector, function(e) {
  					var $this = $(this), 
  						id    = fm.cwdId2Hash($this.attr('id')),
  						ndx;
  					
  					if (!selectLock) {
  						$this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox').prop('checked', false);
  						ndx = $.inArray(id, selectedFiles);
  						if (ndx !== -1) {
  							selectedFiles.splice(ndx, 1);
  							if (cwd.hasClass('elfinder-cwd-allselected')) {
  								selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  								cwd.removeClass('elfinder-cwd-allselected');
  							}
  						}
  					}
  					
  				})
  				// disable files wich removing or moving
  				.on(evtDisable, fileSelector, function() {
  					var $this  = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled), 
  						child  = $this.children(),
  						target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'));
  					
  					child.removeClass(clHover+' '+clSelected);
  					
  					$this.hasClass(clDroppable) && $this.droppable('disable');
  					target.hasClass(clDraggable) && target.draggable('disable');
  				})
  				// if any files was not removed/moved - unlock its
  				.on(evtEnable, fileSelector, function() {
  					var $this  = $(this).removeClass(clDisabled), 
  						target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  					
  					$this.hasClass(clDroppable) && $this.droppable('enable');	
  					target.hasClass(clDraggable) && target.draggable('enable');
  				})
  				.on('scrolltoview', fileSelector, function() {
  					scrollToView($(this));
  				})
  				.on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) {
  					fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type});
  					$(this).toggleClass(clHover, (e.type == 'mouseenter'));
  				})
  				.on('contextmenu.'+fm.namespace, function(e) {
  					var file = $(e.target).closest('.'+clFile);
  					
  					if (file.length && (e.target.nodeName != 'TD' || $.inArray(fm.cwdId2Hash(file.get(0).id), fm.selected()) > -1)) {
  						e.stopPropagation();
  						e.preventDefault();
  						if (!file.hasClass(clDisabled) && !wrapper.data('touching')) {
  							if (!file.hasClass(clSelected)) {
  								unselectAll();
  								file.trigger(evtSelect);
  								trigger();
  							}
  							fm.trigger('contextmenu', {
  								'type'    : 'files',
  								'targets' : fm.selected(),
  								'x'       : e.pageX,
  								'y'       : e.pageY
  							});
  
  						}
  						
  					}
  				})
  				// unselect all on cwd click
  				.on('click.'+fm.namespace, function(e) {
  					if (e.target === this && ! cwd.data('longtap')) {
  						!e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll();
  					}
  				})
  				// prepend fake file/dir
  				.on('create.'+fm.namespace, function(e, file) {
  					var parent = list ? cwd.find('tbody') : cwd,
  						p = parent.find('.elfinder-cwd-parent'),
  						lock = file.move || false,
  						file = $(itemhtml(file)).addClass(clTmp),
  						selected = fm.selected();
  						
  					if (selected.length) {
  						lock && fm.trigger('lockfiles', {files: selected});
  					} else {
  						unselectAll();
  					}
  
  					if (p.length) {
  						p.after(file);
  					} else {
  						parent.prepend(file);
  					}
  					
  					setColwidth();
  					wrapper.scrollTop(0).scrollLeft(0);
  				})
  				// unselect all selected files
  				.on('unselectall', unselectAll)
  				.on('selectfile', function(e, id) {
  					$('#'+fm.cwdHash2Id(id)).trigger(evtSelect);
  					trigger();
  				})
  				.on('colwidth', function() {
  					if (list) {
  						cwd.find('table').css('table-layout', '')
  							.find('td').css('width', '');
  						fixTableHeader({fitWidth: true});
  						fm.storage('cwdColWidth', colWidth = null);
  					}
  				}),
  			wrapper = $('<div class="elfinder-cwd-wrapper"/>')
  				// make cwd itself droppable for folders from nav panel
  				.droppable($.extend({}, droppable, {autoDisable: false}))
  				.on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  				.on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart)
  				.on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend)
  				.on('click.'+fm.namespace, wrapperContextMenu.click)
  				.on('scroll.'+fm.namespace, function() {
  					bufferExt.scrollTimer && clearTimeout(bufferExt.scrollTimer);
  					bufferExt.scrollTimer = setTimeout(function() {
  						wrapper.trigger(scrollEvent);
  					}, 50);
  				}),
  			
  			bottomMarker = $('<div>&nbsp;</div>')
  				.css({position: 'absolute', width: '1px', height: '1px'})
  				.hide(),
  			
  			selectAllCheckbox = selectCheckbox? $('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>')
  				.attr('title', fm.i18n('selectall'))
  				.on('touchstart mousedown click', function(e) {
  					e.stopPropagation();
  					e.preventDefault();
  					if ($(this).data('pending') || e.type === 'click') {
  						return false;
  					}
  					selectAllCheckbox.data('pending', true);
  					if (cwd.hasClass('elfinder-cwd-allselected')) {
  						selectAllCheckbox.find('input').prop('checked', false);
  						setTimeout(function() {
  							unselectAll();
  						}, 10);
  					} else {
  						selectAll();
  					}
  				}) : $(),
  			
  			restm = null,
  			resize = function(init) {
  				var initHeight = function() {
  					var h = 0;
  					wrapper.siblings('div.elfinder-panel:visible').each(function() {
  						h += $(this).outerHeight(true);
  					});
  					wrapper.height(wz.height() - h - wrapper._padding);
  				};
  				
  				init && initHeight();
  				
  				restm && clearTimeout(restm);
  				restm = setTimeout(function(){
  					!init && initHeight();
  					var wph, cwdoh;
  					// fix cwd height if it less then wrapper
  					cwd.css('height', 'auto');
  					wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')),
  					cwdoh = cwd.outerHeight(true);
  					if (cwdoh < wph) {
  						cwd.height(wph);
  					}
  				}, 20);
  				
  				list && ! colResizing && fixTableHeader();
  			},
  			
  			// elfinder node
  			parent = $(this).parent().resize(resize),
  			
  			// workzone node 
  			wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker))
  			;
  
  		// setup by options
  		replacement = $.extend(replacement, options.replacement || {});
  		
  		try {
  			colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null;
  		} catch(e) {
  			colWidth = null;
  		}
  		
  		// setup costomCols
  		if (customCols = fm.storage('cwdCols')) {
  			$.each(options.listView.columns, function(i, n) {
  				if (customCols.indexOf(n) === -1) {
  					customCols.push(n);
  				}
  			});
  			customCols = $.map(customCols, function(n) {
  				return (options.listView.columns.indexOf(n) !== -1)? n : null;
  			});
  		} else {
  			customCols = options.listView.columns;
  		}
  		templates.row = makeTemplateRow();
  		
  		if (mobile) {
  			// for iOS5 bug
  			$('body').on('touchstart touchmove touchend', function(e){});
  		} else {
  			// make files selectable
  			cwd
  				.selectable(selectableOption)
  				.data('selectable', true);
  		}
  		
  		selectCheckbox && cwd.addClass('elfinder-has-checkbox');
  		
  		fm
  			.one('init', function(){
  				var style = document.createElement('style'),
  				sheet;
  				document.head.appendChild(style);
  				sheet = style.sheet;
  				sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', 0);
  				sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', 1);
  				sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', 2);
  				sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:after{ content:"'+fm.i18n('emptySearch')+'" }', 3);
  			})
  			.bind('open add remove searchend', function() {
  				var phash = fm.cwd().hash;
  				cwdHashes = $.map(fm.files(), function(f) { return f.phash == phash ? f.hash : null ;});
  			})
  			.bind('open', function() {
  				cwdParents = fm.parents(fm.cwd().hash);
  				incHashes = void 0;
  				unselectAll();
  				content();
  				resize();
  			})
  			.bind('search', function(e) {
  				cwdHashes = $.map(e.data.files, function(f) { return f.hash; });
  				incHashes = void 0;
  				content();
  				wz.addClass('elfinder-search-result');
  				fm.autoSync('stop');
  				resize();
  			})
  			.bind('searchend', function(e) {
  				wz.removeClass('elfinder-search-result elfinder-cwd-wrapper-empty');
  				if (query || incHashes) {
  					query = '';
  					if (incHashes) {
  						fm.trigger('incsearchend', e.data);
  					}
  					if (!e.data || !e.data.noupdate) {
  						query && unselectAll();
  						content();
  					}
  				}
  				fm.autoSync();
  				resize();
  			})
  			.bind('searchstart', function(e) {
  				unselectAll();
  				query = e.data.query;
  			})
  			.bind('incsearchstart', function(e) {
  				fm.lazy(function() {
  					// incremental search
  					var incquery = e.data.query || '';
  					if (incquery) {
  						var regex = new RegExp(incquery.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i');
  						incHashes = $.map(cwdHashes, function(hash) {
  							var file = fm.file(hash);
  							return (file && file.name.match(regex))? file.hash : null;
  						});
  						fm.trigger('incsearch', { hashes: incHashes, query: incquery })
  							.searchStatus.ininc = true;
  						content();
  						wz.addClass('elfinder-search-result');
  						fm.autoSync('stop');
  					} else {
  						fm.trigger('incsearchend');
  					}
  					resize();
  				});
  			})
  			.bind('incsearchend', function(e) {
  				fm.searchStatus.ininc = false;
  				incHashes = void 0;
  				wz.removeClass('elfinder-search-result');
  				if (!e.data || !e.data.noupdate) {
  					content();
  				}
  				fm.autoSync();
  			})
  			.bind('sortchange', function() {
  				var lastScrollLeft = wrapper.scrollLeft();
  				
  				content();
  				fm.one('cwdrender', function() {
  					wrapper.scrollLeft(lastScrollLeft);
  					selectedFiles.length && trigger();
  					resize();
  				});
  			})
  			.bind('viewchange', function() {
  				var l      = fm.storage('view') == 'list',
  					allsel = cwd.hasClass('elfinder-cwd-allselected');
  				
  				if (l != list) {
  					list = l;
  					fm.viewType = list? 'list' : 'icons';
  					content();
  
  					if (allsel) {
  						cwd.addClass('elfinder-cwd-allselected');
  						selectAllCheckbox.find('input').prop('checked', true);
  					}
  					selectedFiles.length && trigger();
  				}
  				resize();
  			})
  			.bind('resize', function() {
  				var place = list ? cwd.find('tbody') : cwd;
  				resize(true);
  				if (bufferExt.hpi) {
  					bottomMarkerShow(place, place.find('[id]').length);
  				}
  				wz.data('rectangle', $.extend({}, {width: wz.width(), height: wz.height()}, wz.offset()));
  			})
  			.bind('changeclipboard', function(e) {
  				clipCuts = {};
  				if (e.data && e.data.clipboard && e.data.clipboard.length) {
  					$.each(e.data.clipboard, function(i, f) {
  						if (f.cut) {
  							clipCuts[f.hash] = true;
  						}
  					});
  				}
  			})
  			.bind('resMixinMake', function() {
  				setColwidth();
  			})
  			.add(function(e) {
  				var phash = fm.cwd().hash,
  					files = query
  						? $.map(e.data.added || [], function(f) { return f.name.indexOf(query) === -1 ? null : f ;})
  						: $.map(e.data.added || [], function(f) { return f.phash == phash ? f : null; })
  						;
  				add(files);
  				list && resize();
  			})
  			.change(function(e) {
  				var phash = fm.cwd().hash,
  					sel   = fm.selected(),
  					files;
  
  				if (query) {
  					$.each(e.data.changed || [], function(i, file) {
  						remove([file.hash]);
  						if (file.name.indexOf(query) !== -1) {
  							add([file]);
  							$.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  						}
  					});
  				} else {
  					$.each($.map(e.data.changed || [], function(f) { return f.phash == phash ? f : null; }), function(i, file) {
  						remove([file.hash]);
  						add([file]);
  						$.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  					});
  				}
  				
  				trigger();
  			})
  			.remove(function(e) {
  				remove(e.data.removed || []);
  				trigger();
  				if (buffer.length < 1 && (list ? cwd.find('tbody') : cwd).children().length < 1) {
  					wz.addClass('elfinder-cwd-wrapper-empty');
  					selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  				}
  			})
  			// select dragged file if no selected, disable selectable
  			.dragstart(function(e) {
  				var target = $(e.data.target),
  					oe     = e.data.originalEvent;
  
  				wz.data('rectangle', $.extend({}, {width: wz.width(), height: wz.height()}, wz.offset()));
  				if (target.hasClass(fileSelector.substr(1))) {
  					
  					if (!target.hasClass(clSelected)) {
  						!(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll();
  						target.trigger(evtSelect);
  						trigger();
  					}
  				}
  				
  				cwd.selectable('disable').removeClass(clDisabled);
  				selectLock = true;
  			})
  			// enable selectable
  			.dragstop(function() {
  				cwd.selectable('enable');
  				selectLock = false;
  			})
  			.bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) {
  				var events = {
  						lockfiles     : evtDisable ,
  						unlockfiles   : evtEnable ,
  						selectfiles   : evtSelect,
  						unselectfiles : evtUnselect },
  					event  = events[e.type],
  					files  = e.data.files || [],
  					l      = files.length,
  					helper = e.data.helper || $(),
  					parents, ctr;
  
  				if (l > 0) {
  					parents = fm.parents(files[0]);
  				}
  				if (!helper.data('locked')) {
  					while (l--) {
  						$('#'+fm.cwdHash2Id(files[l])).trigger(event);
  					}
  					trigger();
  				}
  				if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) {
  					ctr = e.type !== 'lockfiles';
  					helper.toggleClass('elfinder-drag-helper-plus', ctr);
  					wrapper.toggleClass(clDropActive, ctr);
  				}
  			})
  			// select new files after some actions
  			.bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) {
  				if (e.type == 'upload' && e.data._multiupload) return;
  				var phash = fm.cwd().hash, files;
  				
  				unselectAll();
  
  				$.each((e.data.added || []).concat(e.data.changed || []), function(i, file) { 
  					file && file.phash == phash && selectFile(file.hash);
  				});
  				trigger();
  			})
  			.shortcut({
  				pattern     :'ctrl+a', 
  				description : 'selectall',
  				callback    : selectAll
  			})
  			.shortcut({
  				pattern     : 'left right up down shift+left shift+right shift+up shift+down',
  				description : 'selectfiles',
  				type        : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  				callback    : function(e) { select(e.keyCode, e.shiftKey); }
  			})
  			.shortcut({
  				pattern     : 'home',
  				description : 'selectffile',
  				callback    : function(e) { 
  					unselectAll();
  					scrollToView(cwd.find('[id]:first').trigger(evtSelect));
  					trigger();
  				}
  			})
  			.shortcut({
  				pattern     : 'end',
  				description : 'selectlfile',
  				callback    : function(e) { 
  					unselectAll();
  					scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ;
  					trigger();
  				}
  			});
  		
  	});
  	
  	// fm.timeEnd('cwdLoad')
  	
  	return this;
  };
  
  
  /*
   * File: /js/ui/dialog.js
   */
  
  /**
   * @class  elFinder dialog
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderdialog = function(opts) {
  	var dialog;
  	
  	if (typeof(opts) == 'string' && (dialog = this.closest('.ui-dialog')).length) {
  		if (opts == 'open') {
  			dialog.css('display') == 'none' && dialog.fadeIn(120, function() {
  				dialog.trigger('open');
  			});
  		} else if (opts == 'close') {
  			dialog.stop(true);
  			dialog.css('display') != 'none' && dialog.hide().trigger('close');
  		} else if (opts == 'destroy') {
  			dialog.hide().remove();
  		} else if (opts == 'toTop') {
  			dialog.trigger('totop');
  		} else if (opts == 'posInit') {
  			dialog.trigger('posinit');
  		}
  	}
  	
  	opts = $.extend({}, $.fn.elfinderdialog.defaults, opts);
  
  	this.filter(':not(.ui-dialog-content)').each(function() {
  		var self       = $(this).addClass('ui-dialog-content ui-widget-content'),
  			parent     = self.parent(),
  			clactive   = 'elfinder-dialog-active',
  			cldialog   = 'elfinder-dialog',
  			clnotify   = 'elfinder-dialog-notify',
  			clhover    = 'ui-state-hover',
  			id         = parseInt(Math.random()*1000000),
  			overlay    = parent.children('.elfinder-overlay'),
  			buttonset  = $('<div class="ui-dialog-buttonset"/>'),
  			buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>')
  				.append(buttonset),
  			btnWidth   = 0,
  			platformWin = (window.navigator.platform.indexOf('Win') != -1),
  			
  			dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"/>')
  				.hide()
  				.append(self)
  				.appendTo(parent)
  				.draggable({
  					handle : '.ui-dialog-titlebar',
  					containment : 'document',
  					stop : function(e, ui){
  						dialog.css({height : opts.height});
  						self.data('draged', true);
  					}
  				})
  				.css({
  					width  : opts.width,
  					height : opts.height
  				})
  				.mousedown(function(e) {
  					setTimeout(function() {
  						dialog.is(':visible') && dialog.trigger('totop');
  					}, 10);
  				})
  				.on('open', function() {
  					var d = $(this),
  					maxWinWidth = (d.outerWidth() > parent.width()-10)? parent.width()-10 : null;
  					
  					maxWinWidth && d.css({width: maxWinWidth, left: '5px'});
  
  					if (!dialog.hasClass(clnotify)) {
  						
  						parent.find('.'+cldialog+':visible').not('.'+clnotify).each(function() {
  							var d     = $(this),
  								top   = parseInt(d.css('top')),
  								left  = parseInt(d.css('left')),
  								_top  = parseInt(dialog.css('top')),
  								_left = parseInt(dialog.css('left'))
  								;
  
  							if (d[0] != dialog[0] && (top == _top || left == _left)) {
  								dialog.css({
  									top  : (top+(maxWinWidth? 15 : 10))+'px',
  									left : (maxWinWidth? 5 : left+10)+'px'
  								});
  							}
  						});
  					} 
  					
  					dialog.trigger('totop');
  					
  					dialog.data('modal') && overlay.elfinderoverlay('show');
  					
  					typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])();
  				})
  				.on('close', function() {
  					var dialogs;
  
  					dialog.data('modal') && overlay.elfinderoverlay('hide');
  
  					if (typeof(opts.close) == 'function') {
  						$.proxy(opts.close, self[0])();
  					} else if (opts.destroyOnClose) {
  						dialog.hide().remove();
  					}
  					
  					// get focus to next dialog
  					dialogs = parent.find('.elfinder-dialog:visible');
  					if (dialogs.length) {
  						dialogs.find(':last').trigger('totop');
  					} else {
  						setTimeout(function() {
  							// return focus to parent
  							parent.mousedown().click();
  						}, 20);
  					}
  				})
  				.on('totop', function() {
  					parent.find('.'+cldialog+':visible').removeClass(clactive+' ui-front');
  					dialog.addClass(clactive+' ui-front');
  
  					if (!dialog.find('input,textarea').length) {
  						dialog.find('.ui-button:'+(platformWin? 'first':'last')).focus().end().find(':text:first').focus();
  					}
  				})
  				.on('posinit', function() {
  					var css = opts.position;
  					if (! css && ! dialog.data('resizing')) {
  						css = {
  							top  : Math.max(0, parseInt((parent.height() - dialog.outerHeight())/2 - 42))+'px',
  							left : Math.max(0, parseInt((parent.width() - dialog.outerWidth())/2))+'px'
  						};
  					}
  					css && dialog.css(css);
  				})
  				.data({modal: opts.modal})
  			;
  		
  		dialog.trigger('posinit');
  
  		if (opts.closeOnEscape) {
  			$(document).on('keyup.'+id, function(e) {
  				if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass(clactive)) {
  					self.elfinderdialog('close');
  					$(document).off('keyup.'+id);
  				}
  			})
  		}
  		dialog.prepend(
  			$('<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix">'+opts.title+'</div>')
  				.prepend($('<a href="#" class="ui-dialog-titlebar-close ui-corner-all"><span class="ui-icon ui-icon-closethick"/></a>')
  					.mousedown(function(e) {
  						e.preventDefault();
  						self.elfinderdialog('close');
  					}))
  
  		);
  		
  		$.each(opts.buttons, function(name, cb) {
  			var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+name+'</span></button>')
  				.click($.proxy(cb, self[0]))
  				.hover(function(e) { 
  					if (opts.btnHoverFocus) {
  						$(this)[e.type == 'mouseenter' ? 'focus' : 'blur']();
  					} else {
  						$(this).toggleClass(clhover, e.type == 'mouseenter');
  					}
  				})
  				.focus(function() { $(this).addClass(clhover) })
  				.blur(function() { $(this).removeClass(clhover) })
  				.keydown(function(e) { 
  					var next;
  					e.stopPropagation();
  					if (e.keyCode == $.ui.keyCode.ENTER) {
  						e.preventDefault();
  						$(this).click();
  					}  else if (e.keyCode == $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT) {
  						e.preventDefault();
  						next = $(this).next('.ui-button');
  						next.length ? next.focus() : $(this).parent().children('.ui-button:first').focus();
  					}  else if (e.keyCode == $.ui.keyCode.LEFT) {
  						e.preventDefault();
  						next = $(this).prev('.ui-button');
  						next.length ? next.focus() : $(this).parent().children('.ui-button:last').focus()
  					}
  				})
  			if (platformWin) {
  				buttonset.append(button);
  			} else {
  				buttonset.prepend(button);
  			}
  		});
  		
  		if (buttonset.children().length) {
  			dialog.append(buttonpane);
  			
  			dialog.show();
  			buttonpane.find('button').each(function(i, btn) {
  				btnWidth += $(btn).outerWidth(true);
  			});
  			dialog.hide();
  			btnWidth += 20;
  			
  			if (dialog.width() < btnWidth) {
  				dialog.width(btnWidth);
  			}
  		}
  		
  		if (opts.resizable && $.fn.resizable) {
  			dialog.resizable({
  					minWidth   : opts.minWidth,
  					minHeight  : opts.minHeight,
  					alsoResize : this,
  					start      : function() {
  						if (dialog.data('resizing') !== true && dialog.data('resizing')) {
  							clearTimeout(dialog.data('resizing'));
  						}
  						dialog.data('resizing', true);
  					},
  					stop       : function() {
  						dialog.data('resizing', setTimeout(function() {
  							dialog.data('resizing', false);
  						}, 200));
  					}
  				});
  		} 
  			
  		typeof(opts.create) == 'function' && $.proxy(opts.create, this)();
  			
  		opts.autoOpen && self.elfinderdialog('open');
  
  	});
  	
  	return this;
  };
  
  $.fn.elfinderdialog.defaults = {
  	cssClass  : '',
  	title     : '',
  	modal     : false,
  	resizable : true,
  	autoOpen  : true,
  	closeOnEscape : true,
  	destroyOnClose : false,
  	buttons   : {},
  	btnHoverFocus : true,
  	position  : null,
  	width     : 320,
  	height    : 'auto',
  	minWidth  : 200,
  	minHeight : 110
  };
  
  
  /*
   * File: /js/ui/mkdirbutton.js
   */
  
  /**
   * @class  elFinder toolbar button to switch mkdir mode.
   *
   * @author Naoki Sawada
   **/
  $.fn.elfindermkdirbutton = function(cmd) {
  	return this.each(function() {
  		var button = $(this).elfinderbutton(cmd);
  
  		cmd.change(function() {
  			button.attr('title', cmd.value);
  		});
  	});
  };
  
  
  /*
   * File: /js/ui/navbar.js
   */
  
  /**
   * @class elfindernav - elFinder container for diretories tree and places
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindernavbar = function(fm, opts) {
  
  	this.not('.elfinder-navbar').each(function() {
  		var nav    = $(this).addClass('ui-state-default elfinder-navbar'),
  			parent = nav.parent(),
  			wz     = parent.children('.elfinder-workzone').append(nav),
  			delta  = nav.outerHeight() - nav.height(),
  			ltr    = fm.direction == 'ltr',
  			handle, swipeHandle, autoHide, setWidth;
  
  		fm.bind('resize', function() {
  			nav.height(wz.height() - delta);
  		});
  		
  		if (fm.UA.Touch) {
  			autoHide = fm.storage('autoHide') || {};
  			if (typeof autoHide.navbar === 'undefined') {
  				autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && $.map(opts.autoHideUA, function(v){ return fm.UA[v]? true : null; }).length);
  				fm.storage('autoHide', autoHide);
  			}
  			
  			if (autoHide.navbar) {
  				fm.one('init', function() {
  					fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); });
  				});
  			}
  			
  			fm.bind('load', function() {
  				swipeHandle = $('<div class="elfinder-navbar-swipe-handle"/>').appendTo(wz);
  				if (swipeHandle.css('pointer-events') !== 'none') {
  					swipeHandle.remove();
  					swipeHandle = null;
  				}
  			});
  			
  			nav.on('navshow navhide', function(e, data) {
  				var mode     = (e.type === 'navshow')? 'show' : 'hide',
  					duration = (data && data.duration)? data.duration : 'fast',
  					handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10);
  				nav.stop(true, true)[mode](duration, function() {
  					if (mode === 'show') {
  						swipeHandle && swipeHandle.stop(true, true).hide();
  					} else {
  						if (swipeHandle) {
  							swipeHandle.width(handleW? handleW : '');
  							fm.resources.blink(swipeHandle, 'slowonce');
  						}
  					}
  					fm.trigger('navbar'+ mode);
  					fm.getUI('cwd').trigger('resize');
  					data.init && fm.trigger('uiautohide');
  				});
  				autoHide.navbar = (mode !== 'show');
  				fm.storage('autoHide', $.extend(fm.storage('autoHide'), {navbar: autoHide.navbar}));
  			});
  		}
  		
  		if ($.fn.resizable && ! fm.UA.Mobile) {
  			handle = nav.resizable({
  					handles : ltr ? 'e' : 'w',
  					minWidth : opts.minWidth || 150,
  					maxWidth : opts.maxWidth || 500,
  					stop : function(e, ui) {
  						fm.storage('navbarWidth', ui.size.width);
  					}
  				})
  				.on('resize scroll', function(e) {
  					clearTimeout($(this).data('posinit'));
  					$(this).data('posinit', setTimeout(function() {
  						var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2;
  						handle.css({
  							top  : parseInt(nav.scrollTop())+'px',
  							left : ltr ? 'auto' : parseInt(nav.scrollLeft() + offset),
  							right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto'
  						});
  						if (e.type === 'resize') {
  							fm.getUI('cwd').trigger('resize');
  						}
  					}, 50));
  				})
  				.find('.ui-resizable-handle').addClass('ui-front');
  
  			fm.one('open', function() {
  				setTimeout(function() {
  					nav.trigger('resize');
  				}, 150);
  			});
  		}
  
  		if (setWidth = fm.storage('navbarWidth')) {
  			nav.width(setWidth);
  		} else {
  			if (fm.UA.Mobile) {
  				nav.data('defWidth', nav.width());
  				$(window).on('resize', function(e){
  					setWidth = nav.parent().width() / 2;
  					if (nav.data('defWidth') > setWidth) {
  						nav.width(setWidth);
  					} else {
  						nav.width(nav.data('defWidth'));
  					}
  					nav.data('width', nav.width());
  				});
  			}
  		}
  
  	});
  	
  	return this;
  };
  
  
  /*
   * File: /js/ui/overlay.js
   */
  
  
  $.fn.elfinderoverlay = function(opts) {
  	
  	this.filter(':not(.elfinder-overlay)').each(function() {
  		opts = $.extend({}, opts);
  		$(this).addClass('ui-front ui-widget-overlay elfinder-overlay')
  			.hide()
  			.mousedown(function(e) {
  				e.preventDefault();
  				e.stopPropagation();
  			})
  			.data({
  				cnt  : 0,
  				show : typeof(opts.show) == 'function' ? opts.show : function() { },
  				hide : typeof(opts.hide) == 'function' ? opts.hide : function() { }
  			});
  	});
  	
  	if (opts == 'show') {
  		var o    = this.eq(0),
  			cnt  = o.data('cnt') + 1,
  			show = o.data('show');
  
  		o.data('cnt', cnt);
  
  		if (o.is(':hidden')) {
  			o.show();
  			show();
  		}
  	} 
  	
  	if (opts == 'hide') {
  		var o    = this.eq(0),
  			cnt  = o.data('cnt') - 1,
  			hide = o.data('hide');
  		
  		o.data('cnt', cnt);
  			
  		if (cnt <= 0) {
  			o.hide();
  			hide();
  		}
  	}
  	
  	return this;
  };
  
  
  /*
   * File: /js/ui/panel.js
   */
  
  $.fn.elfinderpanel = function(fm) {
  	
  	return this.each(function() {
  		var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'),
  			margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right');
  		
  		fm.one('load', function(e) {
  			var navbar = fm.getUI('navbar');
  			
  			panel.css(margin, parseInt(navbar.outerWidth(true)));
  			navbar.on('resize', function() {
  				panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true)))
  			})
  		})
  	})
  };
  
  
  /*
   * File: /js/ui/path.js
   */
  
  /**
   * @class elFinder ui
   * Display current folder path in statusbar.
   * Click on folder name in path - open folder
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderpath = function(fm) {
  	return this.each(function() {
  		var query  = '',
  			target = '',
  			mimes  = [],
  			place  = 'statusbar',
  			clHover= fm.res('class', 'hover'),
  			prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-',
  			wzbase = $('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"/>'),
  			path   = $(this).addClass('elfinder-path').html('&nbsp;')
  				.on('mousedown', 'span.elfinder-path-dir', function(e) {
  					var hash = $(this).attr('id').substr(prefix.length);
  					e.preventDefault();
  					if (hash != fm.cwd().hash) {
  						$(this).addClass(clHover);
  						if (query) {
  							fm.exec('search', query, { target: hash, mime: mimes.join(' ') });
  						} else {
  							fm.exec('open', hash);
  						}
  					}
  				})
  				.prependTo(fm.getUI('statusbar').show()),
  			roots = $('<div class="elfinder-path-roots"/>').on('click', function(e) {
  				e.stopPropagation();
  				e.preventDefault();
  				
  				var roots = $.map(fm.roots, function(h) { return fm.file(h)}),
  				raw = [];
  
  				$.each(roots, function(i, f) {
  					if (fm.root(fm.cwd().hash) !== f.hash) {
  						raw.push({
  							label    : f.name,
  							icon     : 'home',
  							remain   : true,
  							callback : function() { fm.exec('open', f.hash); }
  						});
  					}
  				});
  				fm.trigger('contextmenu', {
  					raw: raw,
  					x: e.pageX,
  					y: e.pageY
  				});
  			}).append('<span class="elfinder-button-icon elfinder-button-icon-menu" />').appendTo(wzbase),
  			render = function(cwd) {
  				var dirs = [];
  				$.each(fm.parents(cwd), function(i, hash) {
  					var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir',
  						name = fm.escape(fm.file(hash).name);
  					dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+name+'">'+name+'</span>');
  				});
  				return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>');
  			},
  			toWorkzone = function() {
  				var prev;
  				path.children('span.elfinder-path-dir').attr('style', '');
  				prev = fm.direction === 'ltr'? $('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : $();
  				path.scrollLeft(prev.length? prev.position().left : 0);
  			},
  			fit = function() {
  				var dirs = path.children('span.elfinder-path-dir'),
  					cnt  = dirs.length,
  					m, bg = 0, ids;
  				
  				if (place === 'workzone' || cnt < 2) {
  					dirs.attr('style', '');
  					return;
  				}
  				path.width(path.css('max-width'));
  				dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'});
  				m = path.width() - 9;
  				path.children('span.elfinder-path-other').each(function() {
  					m -= $(this).width();
  				});
  				ids = [];
  				dirs.each(function(i) {
  					var dir = $(this),
  						w   = dir.width();
  					m -= w;
  					if (w < this.scrollWidth) {
  						ids.push(i);
  					}
  				});
  				path.width('');
  				if (ids.length) {
  					if (m > 0) {
  						m = m / ids.length;
  						$.each(ids, function(i, k) {
  							var d = $(dirs[k]);
  							d.css('max-width', d.width() + m);
  						});
  					}
  					dirs.last().attr('style', '');
  				} else {
  					dirs.attr('style', '');
  				}
  			};
  
  			fm.bind('open searchend parents', function() {
  				var dirs = [];
  
  				query  = '';
  				target = '';
  				mimes  = [];
  				
  				path.html(render(fm.cwd().hash));
  				if (Object.keys(fm.roots).length > 1) {
  					path.css('margin', '');
  					roots.show();
  				} else {
  					path.css('margin', 0);
  					roots.hide();
  				}
  				fit();
  			})
  			.bind('searchstart', function(e) {
  				if (e.data) {
  					query  = e.data.query || '';
  					target = e.data.target || '';
  					mimes  = e.data.mimes || []
  				}
  			})
  			.bind('search', function(e) {
  				var dirs = [],
  					html = '';
  				if (target) {
  					html = render(target);
  				} else {
  					html = fm.i18n('btnAll');
  				}
  				path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html);
  				fit();
  			})
  			// on swipe to navbar show/hide
  			.bind('navbarshow navbarhide', function(e) {
  				var wz = fm.getUI('workzone');
  				if (e.type === 'navbarshow') {
  					wz.height(wz.height() + wzbase.outerHeight());
  					path.prependTo(fm.getUI('statusbar'));
  					wzbase.detach();
  					place = 'statusbar';
  					fm.unbind('open', toWorkzone);
  				} else {
  					wzbase.append(path).insertBefore(wz);
  					wz.height(wz.height() - wzbase.outerHeight());
  					place = 'workzone';
  					toWorkzone();
  					fm.bind('open', toWorkzone);
  				}
  				setTimeout(function() { fm.trigger('resize'); }, 0);
  			})
  			.bind('resize', fit);
  	});
  };
  
  
  /*
   * File: /js/ui/places.js
   */
  
  /**
   * @class elFinder places/favorites ui
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderplaces = function(fm, opts) {
  	return this.each(function() {
  		var dirs      = {},
  			c         = 'class',
  			navdir    = fm.res(c, 'navdir'),
  			collapsed = fm.res(c, 'navcollapse'),
  			expanded  = fm.res(c, 'navexpand'),
  			hover     = fm.res(c, 'hover'),
  			clroot    = fm.res(c, 'treeroot'),
  			dropover  = fm.res(c, 'adroppable'),
  			tpl       = fm.res('tpl', 'placedir'),
  			ptpl      = fm.res('tpl', 'perms'),
  			spinner   = $(fm.res('tpl', 'navspinner')),
  			key       = 'places'+(opts.suffix? opts.suffix : ''),
  			menuTimer = null,
  			/**
  			 * Convert places dir node into dir hash
  			 *
  			 * @param  String  directory id
  			 * @return String
  			 **/
  			id2hash   = function(id) { return id.substr(6);	},
  			/**
  			 * Convert places dir node into dir hash
  			 *
  			 * @param  String  directory id
  			 * @return String
  			 **/
  			hash2id   = function(hash) { return 'place-'+hash; },
  			
  			/**
  			 * Save current places state
  			 *
  			 * @return void
  			 **/
  			save      = function() {
  				var hashes = [], data = {};
  				
  				hashes = $.map(subtree.children().find('[id]'), function(n) {
  					return id2hash(n.id);
  				});
  				if (hashes.length) {
  					$.each(hashes.reverse(), function(i, h) {
  						data[h] = dirs[h];
  					});
  				} else {
  					data = null;
  				}
  				
  				fm.storage(key, data);
  			},
  			/**
  			 * Return node for given dir object
  			 *
  			 * @param  Object  directory object
  			 * @return jQuery
  			 **/
  			create    = function(dir, hash) {
  				return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash))
  						.replace(/\{name\}/, fm.escape(dir? dir.name : hash))
  						.replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '')) : '')
  						.replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '')
  						.replace(/\{title\}/, (dir && dir.path)? fm.escape(dir.path) : '')
  						.replace(/\{symlink\}/, '')
  						.replace(/\{style\}/, ''));
  			},
  			/**
  			 * Add new node into places
  			 *
  			 * @param  Object  directory object
  			 * @return void
  			 **/
  			add = function(dir) {
  				var node, hash;
  
  				if (dir.mime !== 'directory') {
  					return false;
  				}
  				hash = dir.hash;
  				if (!fm.files().hasOwnProperty(hash)) {
  					// update cache
  					fm.trigger('tree', {tree: [dir]});
  				}
  				
  				node = create(dir, hash);
  				
  				dirs[hash] = dir;
  				subtree.prepend(node);
  				root.addClass(collapsed);
  				
  				return true;
  			},
  			/**
  			 * Remove dir from places
  			 *
  			 * @param  String  directory hash
  			 * @return String  removed name
  			 **/
  			remove = function(hash) {
  				var name = null, tgt;
  
  				if (dirs[hash]) {
  					delete dirs[hash];
  					tgt = $('#'+hash2id(hash));
  					if (tgt.length) {
  						name = tgt.text();
  						tgt.parent().remove();
  						if (!subtree.children().length) {
  							root.removeClass(collapsed);
  							places.removeClass(expanded);
  							subtree.slideToggle(false);
  						}
  					}
  				}
  				
  				return name;
  			},
  			/**
  			 * Move up dir on places
  			 *
  			 * @param  String  directory hash
  			 * @return void
  			 **/
  			moveup = function(hash) {
  				var self = $('#'+hash2id(hash)),
  					tgt  = self.parent(),
  					prev = tgt.prev('div'),
  					cls  = 'ui-state-hover',
  					ctm  = fm.getUI('contextmenu');
  				
  				menuTimer && clearTimeout(menuTimer);
  				
  				if (prev.length) {
  					ctm.find(':first').data('placesHash', hash);
  					self.addClass(cls);
  					tgt.insertBefore(prev);
  					prev = tgt.prev('div');
  					menuTimer = setTimeout(function() {
  						self.removeClass(cls);
  						if (ctm.find(':first').data('placesHash') === hash) {
  							ctm.hide().empty();
  						}
  					}, 1500);
  				}
  				
  				if(!prev.length) {
  					self.removeClass(cls);
  					ctm.hide().empty();
  				}
  			},
  			/**
  			 * Update dir at places
  			 *
  			 * @param  Object   directory
  			 * @param  String   previous hash
  			 * @return Boolean
  			 **/
  			update = function(dir, preHash) {
  				var hash = dir.hash,
  					tgt  = $('#'+hash2id(preHash || hash)),
  					node = create(dir, hash);
  
  				if (tgt.length > 0) {
  					tgt.parent().replaceWith(node);
  					dirs[hash] = dir;
  					return true
  				} else {
  					return false;
  				}
  			},
  			/**
  			 * Remove all dir from places
  			 *
  			 * @return void
  			 **/
  			clear = function() {
  				subtree.empty();
  				root.removeClass(collapsed);
  				places.removeClass(expanded);
  				subtree.slideToggle(false);
  			},
  			/**
  			 * Sort places dirs A-Z
  			 *
  			 * @return void
  			 **/
  			sort = function() {
  				$.each(dirs, function(h, f) {
  					var dir = fm.file(h) || f,
  						node = create(dir, h),
  						ret = null;
  					if (!dir) {
  						node.hide();
  					}
  					if (subtree.children().length) {
  						$.each(subtree.children(), function() {
  							var current =  $(this);
  							if (dir.name.localeCompare(current.children('.'+navdir).text()) < 0) {
  								ret = !node.insertBefore(current);
  								return ret;
  							}
  						});
  						if (ret !== null) {
  							return true;
  						}
  					}
  					!$('#'+hash2id(h)).length && subtree.append(node);
  				});
  				save();
  			},
  			/**
  			 * Node - wrapper for places root
  			 *
  			 * @type jQuery
  			 **/
  			wrapper = create({
  					hash  : 'root-'+fm.namespace, 
  					name  : fm.i18n(opts.name, 'places'),
  					read  : true,
  					write : true
  				}),
  			/**
  			 * Places root node
  			 *
  			 * @type jQuery
  			 **/
  			root = wrapper.children('.'+navdir)
  				.addClass(clroot)
  				.click(function(e) {
  					e.stopPropagation();
  					if (root.hasClass(collapsed)) {
  						places.toggleClass(expanded);
  						subtree.slideToggle();
  						fm.storage('placesState', places.hasClass(expanded)? 1 : 0);
  					}
  				})
  				.append(
  					// sort button
  					$('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>')
  						.on('click', function(e) {
  							e.stopPropagation();
  							subtree.empty();
  							sort();
  						}
  					)
  				),
  			/**
  			 * Container for dirs
  			 *
  			 * @type jQuery
  			 **/
  			subtree = wrapper.children('.'+fm.res(c, 'navsubtree')),
  			
  			/**
  			 * Main places container
  			 *
  			 * @type jQuery
  			 **/
  			places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all')
  				.hide()
  				.append(wrapper)
  				.appendTo(fm.getUI('navbar'))
  				.on('mouseenter mouseleave', '.'+navdir, function(e) {
  					$(this).toggleClass('ui-state-hover', (e.type == 'mouseenter'));
  				})
  				.on('click', '.'+navdir, function(e) {
  					var p = $(this);
  					if (p.data('longtap')) {
  						e.stopPropagation();
  						return;
  					}
  					! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6));
  				})
  				.on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) {
  					var self = $(this),
  						hash = self.attr('id').substr(6);
  					
  					e.preventDefault();
  
  					fm.trigger('contextmenu', {
  						raw : [{
  							label    : fm.i18n('moveUp'),
  							icon     : 'up',
  							remain   : true,
  							callback : function() { moveup(hash); save(); }
  						},'|',{
  							label    : fm.i18n('rmFromPlaces'),
  							icon     : 'rm',
  							callback : function() { remove(hash); save(); }
  						}],
  						'x'       : e.pageX,
  						'y'       : e.pageY
  					});
  					
  					self.addClass('ui-state-hover');
  					
  					fm.getUI('contextmenu').children().on('mouseenter', function() {
  						self.addClass('ui-state-hover');
  					});
  					
  					fm.bind('closecontextmenu', function() {
  						self.removeClass('ui-state-hover');
  					});
  				})
  				.droppable({
  					tolerance  : 'pointer',
  					accept     : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file',
  					hoverClass : fm.res('class', 'adroppable'),
  					classes    : { // Deprecated hoverClass jQueryUI>=1.12.0
  						'ui-droppable-hover': fm.res('class', 'adroppable')
  					},
  					over       : function(e, ui) {
  						var helper = ui.helper,
  							dir    = $.map(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? h : null});
  						e.stopPropagation();
  						helper.data('dropover', helper.data('dropover') + 1);
  						if (fm.insideWorkzone(e.pageX, e.pageY)) {
  							if (dir.length > 0) {
  								helper.addClass('elfinder-drag-helper-plus');
  								fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper});
  							} else {
  								$(this).removeClass(dropover);
  							}
  						}
  					},
  					out : function(e, ui) {
  						var helper = ui.helper,
  							ctr    = (e.shiftKey||e.ctrlKey||e.metaKey);
  						e.stopPropagation();
  						helper.toggleClass('elfinder-drag-helper-move elfinder-drag-helper-plus', helper.data('locked')? true : ctr).data('dropover', Math.max(helper.data('dropover') - 1, 0));
  						$(this).removeData('dropover')
  						       .removeClass(dropover);
  						fm.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : helper.data('files'), helper: helper});
  					},
  					drop       : function(e, ui) {
  						var helper  = ui.helper,
  							resolve = true;
  						
  						$.each(helper.data('files'), function(i, hash) {
  							var dir = fm.file(hash);
  							
  							if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  								add(dir);
  							} else {
  								resolve = false;
  							}
  						})
  						save();
  						resolve && helper.hide();
  					}
  				})
  				// for touch device
  				.on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) {
  					if (e.originalEvent.touches.length > 1) {
  						return;
  					}
  					var hash = $(this).attr('id').substr(6),
  					p = $(this)
  					.addClass(hover)
  					.data('longtap', null)
  					.data('tmlongtap', setTimeout(function(){
  						// long tap
  						p.data('longtap', true);
  						fm.trigger('contextmenu', {
  							raw : [{
  								label    : fm.i18n('rmFromPlaces'),
  								icon     : 'rm',
  								callback : function() { remove(hash); save(); }
  							}],
  							'x'       : e.originalEvent.touches[0].pageX,
  							'y'       : e.originalEvent.touches[0].pageY
  						});
  					}, 500));
  				})
  				.on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) {
  					clearTimeout($(this).data('tmlongtap'));
  					if (e.type == 'touchmove') {
  						$(this).removeClass(hover);
  					}
  				});
  
  		if ($.fn.sortable) {
  			subtree.addClass('touch-punch')
  			.sortable({
  				appendTo : fm.getUI(),
  				revert   : false,
  				helper   : function(e) {
  					var dir = $(e.target).parent();
  						
  					dir.children().removeClass('ui-state-hover');
  					
  					return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>')
  							.append($('<div class="elfinder-navbar"/>').show().append(dir.clone()));
  
  				},
  				stop     : function(e, ui) {
  					var target = $(ui.item[0]),
  						top    = places.offset().top,
  						left   = places.offset().left,
  						width  = places.width(),
  						height = places.height(),
  						x      = e.pageX,
  						y      = e.pageY;
  					
  					if (!(x > left && x < left+width && y > top && y < y+height)) {
  						remove(id2hash(target.children(':first').attr('id')));
  						save();
  					}
  				},
  				update   : function(e, ui) {
  					save();
  				}
  			});
  		}
  
  		// "on regist" for command exec
  		$(this).on('regist', function(e, files){
  			var added = false;
  			$.each(files, function(i, dir) {
  				if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  					if (add(dir)) {
  						added = true;
  					}
  				}
  			});
  			added && save();
  		});
  	
  
  		// on fm load - show places and load files from backend
  		fm.one('load', function() {
  			var dat, hashes;
  			
  			if (fm.oldAPI) {
  				return;
  			}
  			
  			places.show().parent().show();
  
  			dirs = {};
  			dat = fm.storage(key);
  			if (typeof dat === 'string') {
  				// old data type elFinder <= 2.1.12
  				dat = $.map(dat.split(','), function(hash) { return hash || null;});
  				$.each(dat, function(i, d) {
  					var dir = d.split('#')
  					dirs[dir[0]] = dir[1]? dir[1] : dir[0];
  				});
  			} else if ($.isPlainObject(dat)) {
  				dirs = dat;
  			}
  			// allow modify `dirs`
  			/**
  			 * example for preset places
  			 * 
  			 * elfinderInstance.bind('placesload', function(e, fm) {
  			 * 	//if (fm.storage(e.data.storageKey) === null) { // for first time only
  			 * 	if (!fm.storage(e.data.storageKey)) {           // for empty places
  			 * 		e.data.dirs[targetHash] = fallbackName;     // preset folder
  			 * 	}
  			 * }
  			 **/
  			fm.trigger('placesload', {dirs: dirs, storageKey: key}, true);
  			
  			hashes = Object.keys(dirs);
  			if (hashes.length) {
  				root.prepend(spinner);
  				
  				fm.request({
  					data : {cmd : 'info', targets : hashes},
  					preventDefault : true
  				})
  				.done(function(data) {
  					var exists = {};
  					$.each(data.files, function(i, f) {
  						var hash = f.hash;
  						exists[hash] = f;
  					});
  					$.each(dirs, function(h, f) {
  						add(exists[h] || $.extend({notfound: true}, f));
  					});
  					if (fm.storage('placesState') > 0) {
  						root.click();
  					}
  				})
  				.always(function() {
  					spinner.remove();
  				})
  			}
  			
  
  			fm.change(function(e) {
  				var changed = false;
  				$.each(e.data.changed, function(i, file) {
  					if (dirs[file.hash]) {
  						if (file.mime !== 'directory') {
  							if (remove(file.hash)) {
  								changed = true;
  							}
  						} else {
  							if (update(file)) {
  								changed = true;
  							}
  						}
  					}
  				});
  				changed && save();
  			})
  			.bind('rename', function(e) {
  				var changed = false;
  				if (e.data.removed) {
  					$.each(e.data.removed, function(i, hash) {
  						if (e.data.added[i]) {
  							if (update(e.data.added[i], hash)) {
  								changed = true;
  							}
  						}
  					});
  				}
  				changed && save();
  			})
  			.bind('rm paste', function(e) {
  				var names = [],
  					changed = false;
  				if (e.data.removed) {
  					$.each(e.data.removed, function(i, hash) {
  						var name = remove(hash);
  						name && names.push(name);
  					});
  				}
  				if (names.length) {
  					changed = true;
  				}
  				if (e.data.added && names.length) {
  					$.each(e.data.added, function(i, file) {
  						if ($.inArray(file.name, names) !== 1) {
  							file.mime == 'directory' && add(file);
  						}
  					});
  				}
  				changed && save();
  			})
  			.bind('sync netmount', function(e) {
  				var hashes = Object.keys(dirs);
  				if (hashes.length) {
  					root.prepend(spinner);
  
  					fm.request({
  						data : {cmd : 'info', targets : hashes},
  						preventDefault : true
  					})
  					.done(function(data) {
  						var exists  = {},
  							updated = false,
  							cwd     = fm.cwd().hash;
  						$.each(data.files || [], function(i, file) {
  							var hash = file.hash;
  							exists[hash] = file;
  							if (!fm.files().hasOwnProperty(file.hash)) {
  								// update cache
  								fm.trigger('tree', {tree: [file]});
  							}
  						});
  						$.each(dirs, function(h, f) {
  							if (!f.notfound != !!exists[h]) {
  								if ((f.phash === cwd && e.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) {
  									if (remove(h)) {
  										updated = true;
  									}
  								} else {
  									if (update(exists[h] || $.extend({notfound: true}, f))) {
  										updated = true;
  									}
  								}
  							} else if (exists[h] && exists[h].phash != cwd) {
  								// update permission of except cwd
  								update(exists[h]);
  							}
  						});
  						updated && save();
  					})
  					.always(function() {
  						spinner.remove();
  					});
  				}
  			})
  			
  		})
  		
  	});
  };
  
  
  /*
   * File: /js/ui/searchbutton.js
   */
  
  /**
   * @class  elFinder toolbar search button widget.
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindersearchbutton = function(cmd) {
  	return this.each(function() {
  		var result = false,
  			fm     = cmd.fm,
  			isopts = cmd.options.incsearch || { enable: false },
  			id     = function(name){return fm.namespace + name},
  			toolbar= fm.getUI('toolbar'),
  			btnCls = fm.res('class', 'searchbtn'),
  			button = $(this).hide().addClass('ui-widget-content elfinder-button '+btnCls),
  			search = function() {
  				input.data('inctm') && clearTimeout(input.data('inctm'));
  				opts && opts.slideUp();
  				var val = $.trim(input.val()),
  					from = !$('#' + id('SearchFromAll')).prop('checked'),
  					mime = $('#' + id('SearchMime')).prop('checked');
  				if (from) {
  					if ($('#' + id('SearchFromVol')).prop('checked')) {
  						from = fm.root(fm.cwd().hash);
  					} else {
  						from = fm.cwd().hash;
  					}
  				}
  				if (mime) {
  					mime = val;
  					val = '.';
  				}
  				if (val) {
  					cmd.exec(val, from, mime).done(function() {
  						result = true;
  						input.focus();
  					}).fail(function() {
  						abort();
  					});
  					
  				} else {
  					fm.trigger('searchend');
  				}
  			},
  			abort = function() {
  				input.data('inctm') && clearTimeout(input.data('inctm'));
  				input.val('').blur();
  				if (result || incVal) {
  					result = false;
  					incVal = '';
  					fm.lazy(function() {
  						fm.trigger('searchend');
  					});
  				}
  			},
  			incVal = '',
  			input  = $('<input type="text" size="42"/>')
  				.on('focus', function() {
  					incVal = '';
  					opts && opts.slideDown();
  				})
  				.on('blur', function(){
  					if (opts) {
  						if (!opts.data('infocus')) {
  							opts.slideUp();
  						} else {
  							opts.data('infocus', false);
  						}
  					}
  				})
  				.appendTo(button)
  				// to avoid fm shortcuts on arrows
  				.on('keypress', function(e) {
  					e.stopPropagation();
  				})
  				.on('keydown', function(e) {
  					e.stopPropagation();
  					
  					e.keyCode == 13 && search();
  					
  					if (e.keyCode== 27) {
  						e.preventDefault();
  						abort();
  					}
  				}),
  			opts;
  		
  		if (isopts.enable) {
  			isopts.minlen = isopts.minlen || 2;
  			isopts.wait = isopts.wait || 500;
  			input
  				.attr('title', fm.i18n('incSearchOnly'))
  				.on('compositionstart', function() {
  					input.data('composing', true);
  				})
  				.on('compositionend', function() {
  					input.removeData('composing');
  					input.trigger('input'); // for IE, edge
  				})
  				.on('input', function() {
  					if (! input.data('composing')) {
  						input.data('inctm') && clearTimeout(input.data('inctm'));
  						input.data('inctm', setTimeout(function() {
  							var val = input.val();
  							if (val.length === 0 || val.length >= isopts.minlen) {
  								(incVal !== val) && fm.trigger('incsearchstart', { query: val });
  								incVal = val;
  							}
  						}, isopts.wait));
  					}
  				});
  			
  			if (fm.UA.ltIE8) {
  				input.on('keydown', function(e) {
  						if (e.keyCode === 229) {
  							input.data('imetm') && clearTimeout(input.data('imetm'));
  							input.data('composing', true);
  							input.data('imetm', setTimeout(function() {
  								input.removeData('composing');
  							}, 100));
  						}
  					})
  					.on('keyup', function(e) {
  						input.data('imetm') && clearTimeout(input.data('imetm'));
  						if (input.data('composing')) {
  							e.keyCode === 13 && input.trigger('compositionend');
  						} else {
  							input.trigger('input');
  						}
  					});
  			}
  		}
  		
  		$('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>')
  			.appendTo(button)
  			.click(search);
  		
  		$('<span class="ui-icon ui-icon-close"/>')
  			.appendTo(button)
  			.click(abort);
  		
  		// wait when button will be added to DOM
  		toolbar.on('load', function(){
  			var parent = button.parent();
  			if (parent.length) {
  				toolbar.prepend(button.show());
  				parent.remove();
  				// position icons for ie7
  				if (fm.UA.ltIE7) {
  					var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search');
  					icon.css({
  						right : '',
  						left  : parseInt(button.width())-icon.outerWidth(true)
  					});
  				}
  				fm.resize();
  			}
  		});
  		
  		fm
  			.one('open', function() {
  				opts = (fm.api < 2.1)? null : $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  					.append(
  						$('<div class="buttonset"/>')
  							.append(
  								$('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'),
  								$('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'),
  								$('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>')
  							),
  						$('<div class="buttonset"/>')
  							.append(
  								$('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>'),
  								$('<input id="'+id('SearchMime')+'" name="serchcol" type="radio"/><label for="'+id('SearchMime')+'">'+fm.i18n('btnMime')+'</label>')
  							)
  					)
  					.hide()
  					.css('overflow', 'hidden')
  					.appendTo(button);
  				if (opts) {
  					opts.find('div.buttonset').buttonset();
  					$('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll')));
  					$('#'+id('SearchMime')).next('label').attr('title', fm.i18n('searchMime'));
  					opts.on('mousedown', 'div.buttonset', function(e){
  							e.stopPropagation();
  							opts.data('infocus', true);
  						})
  						.on('click', 'input', function(e) {
  							e.stopPropagation();
  							$.trim(input.val()) && search();
  						});
  				}
  			})
  			.select(function() {
  				input.blur();
  			})
  			.bind('searchend', function() {
  				input.val('');
  			})
  			.bind('open parents', function() {
  				var dirs    = [],
  					volroot = fm.file(fm.root(fm.cwd().hash));
  				
  				if (volroot) {
  					$.each(fm.parents(fm.cwd().hash), function(i, hash) {
  						dirs.push(fm.file(hash).name);
  					});
  		
  					$('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator'))));
  					$('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name));
  				}
  			})
  			.shortcut({
  				pattern     : 'ctrl+f f3',
  				description : cmd.title,
  				callback    : function() { 
  					input.select().focus();
  				}
  			});
  
  	});
  };
  
  
  /*
   * File: /js/ui/sortbutton.js
   */
  
  /**
   * @class  elFinder toolbar button menu with sort variants.
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindersortbutton = function(cmd) {
  	
  	return this.each(function() {
  		var fm       = cmd.fm,
  			name     = cmd.name,
  			c        = 'class',
  			disabled = fm.res(c, 'disabled'),
  			hover    = fm.res(c, 'hover'),
  			item     = 'elfinder-button-menu-item',
  			selected = item+'-selected',
  			asc      = selected+'-asc',
  			desc     = selected+'-desc',
  			button   = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name)
  				.attr('title', cmd.title)
  				.append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>')
  				.hover(function(e) { !button.hasClass(disabled) && button.toggleClass(hover); })
  				.click(function(e) {
  					if (!button.hasClass(disabled)) {
  						e.stopPropagation();
  						menu.is(':hidden') && cmd.fm.getUI().click();
  						menu.slideToggle(100);
  					}
  				}),
  			menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  				.hide()
  				.appendTo(button)
  				.on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover) })
  				.on('click', '.'+item, function(e) {
  					e.preventDefault();
  					e.stopPropagation();
  					hide();
  				}),
  			update = function() {
  				menu.children(':not(:last)').removeClass(selected+' '+asc+' '+desc)
  					.filter('[rel="'+fm.sortType+'"]')
  					.addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc));
  
  				menu.children(':last').toggleClass(selected, fm.sortStickFolders);
  			},
  			hide = function() { menu.hide(); };
  			
  			
  		$.each(fm.sortRules, function(name, value) {
  			menu.append($('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"/><span class="ui-icon ui-icon-arrowthick-1-s"/>'+fm.i18n('sort'+name)+'</div>').data('type', name));
  		});
  		
  		menu.children().click(function(e) {
  			var type = $(this).attr('rel');
  			
  			cmd.exec([], {
  				type  : type, 
  				order : type == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder, 
  				stick : fm.sortStickFolders
  			});
  		});
  		
  		$('<div class="'+item+' '+item+'-separated"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>')
  			.appendTo(menu)
  			.click(function() {
  				cmd.exec([], {type : fm.sortType, order : fm.sortOrder, stick : !fm.sortStickFolders});
  			});		
  		
  		fm.bind('disable select', hide).getUI().click(hide);
  			
  		fm.bind('sortchange', update)
  		
  		if (menu.children().length > 1) {
  			cmd.change(function() {
  					button.toggleClass(disabled, cmd.disabled());
  					update();
  				})
  				.change();
  			
  		} else {
  			button.addClass(disabled);
  		}
  
  	});
  	
  };
  
  
  /*
   * File: /js/ui/stat.js
   */
  
  /**
   * @class elFinder ui
   * Display number of files/selected files and its size in statusbar
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderstat = function(fm) {
  	return this.each(function() {
  		var size       = $(this).addClass('elfinder-stat-size'),
  			sel        = $('<div class="elfinder-stat-selected"/>')
  				.on('click', 'a', function(e) {
  					var hash = $(this).data('hash');
  					e.preventDefault();
  					fm.exec('opendir', [ hash ]);
  				}),
  			titlesize  = fm.i18n('size'),
  			titleitems = fm.i18n('items'),
  			titlesel   = fm.i18n('selected'),
  			setstat    = function(files, cwd) {
  				var c = 0, 
  					s = 0;
  
  				$.each(files, function(i, file) {
  					if (!cwd || file.phash == cwd) {
  						c++;
  						s += parseInt(file.size)||0;
  					}
  				})
  				size.html(titleitems+': <span class="elfinder-stat-incsearch"></span>'+c+', '+titlesize+': '+fm.formatSize(s));
  			},
  			setIncsearchStat = function(data) {
  				size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : '');
  			},
  			search = false;
  
  		fm.getUI('statusbar').prepend(size).append(sel).show();
  		
  		fm
  		.bind('open reload add remove change searchend', function() {
  			setstat(fm.files(), fm.cwd().hash);
  		})
  		.bind('searchend', function() {
  			search = false;
  		})
  		.search(function(e) {
  			search = true;
  			setstat(e.data.files);
  		})
  		.select(function() {
  			var s = 0,
  				c = 0,
  				files = fm.selectedFiles(),
  				dirs = [],
  				file;
  
  			if (files.length == 1) {
  				file = files[0];
  				s = file.size;
  				if (search) {
  					dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'">'+(file.path? file.path.replace(/\/[^\/]*$/, '') : '..')+'</a>');
  				}
  				dirs.push(fm.escape(file.name));
  				sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : ''));
  				
  				return;
  			}
  
  			$.each(files, function(i, file) {
  				c++;
  				s += parseInt(file.size)||0;
  			});
  
  			sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : '&nbsp;');
  		})
  		.bind('incsearch', function(e) {
  			setIncsearchStat(e.data);
  		})
  		.bind('incsearchend', function() {
  			setIncsearchStat();
  		})
  		;
  	})
  };
  
  
  /*
   * File: /js/ui/toolbar.js
   */
  
  /**
   * @class  elFinder toolbar
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindertoolbar = function(fm, opts) {
  	this.not('.elfinder-toolbar').each(function() {
  		var commands = fm._commands,
  			self     = $(this).addClass('ui-helper-clearfix ui-widget-header ui-corner-top elfinder-toolbar'),
  			options  = {
  				// default options
  				autoHideUA: ['Mobile']
  			},
  			filter   = function(opts) {
  				return $.map(opts, function(v) {
  					if ($.isPlainObject(v)) {
  						options = $.extend(options, v);
  						return null;
  					}
  					return [v];
  				});
  			},
  			render = function(disabled){
  				var name;
  				$.each(buttons, function(i, b) { b.detach(); });
  				self.empty();
  				l = panels.length;
  				while (l--) {
  					if (panels[l]) {
  						panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  						i = panels[l].length;
  						while (i--) {
  							name = panels[l][i];
  							if ((!disabled || $.inArray(name, disabled) === -1) && (cmd = commands[name])) {
  								button = 'elfinder'+cmd.options.ui;
  								if (! buttons[name] && $.fn[button]) {
  									buttons[name] = $('<div/>')[button](cmd);
  								}
  								buttons[name] && panel.prepend(buttons[name]);
  							}
  						}
  						
  						panel.children().length && self.prepend(panel);
  						panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>');
  
  					}
  				}
  				
  				(! self.data('swipeClose') && self.children().length)? self.show() : self.hide();
  				self.trigger('load');
  			},
  			buttons = {},
  			panels   = filter(opts || []),
  			dispre   = null,
  			uiCmdMapPrev = '',
  			l, i, cmd, panel, button, swipeHandle, autoHide;
  		
  		self.prev().length && self.parent().prepend(this);
  		
  		render();
  		
  		fm.bind('open sync', function(){
  			var repCmds = [],
  			disabled = fm.option('disabled');
  
  			if (!dispre || dispre.toString() !== disabled.sort().toString()) {
  				render(disabled && disabled.length? disabled : null);
  			}
  			dispre = disabled.concat().sort();
  
  			if (uiCmdMapPrev !== JSON.stringify(fm.commandMap)) {
  				uiCmdMapPrev = JSON.stringify(fm.commandMap);
  				if (Object.keys(fm.commandMap).length) {
  					$.each(fm.commandMap, function(from, to){
  						var cmd = fm._commands[to],
  						button = cmd? 'elfinder'+cmd.options.ui : null;
  						if (button && $.fn[button]) {
  							repCmds.push(from);
  							var btn = $('div.elfinder-buttonset div.elfinder-button').has('span.elfinder-button-icon-'+from);
  							if (btn.length && !btn.next().has('span.elfinder-button-icon-'+to).length) {
  								if (! buttons[to] && $.fn[button]) {
  									buttons[to] = $('<div/>')[button](fm._commands[to]);
  								}
  								if (buttons[to]) {
  									btn.after(buttons[to].data('origin', from));
  									btn.hide();
  								}
  							}
  						}
  					});
  				}
  				// reset toolbar
  				$.each($('div.elfinder-button'), function(){
  					var origin = $(this).data('origin');
  					if (origin && $.inArray(origin, repCmds) == -1) {
  						$('span.elfinder-button-icon-' + origin).parent().show();
  						$(this).detach();
  					}
  				});
  			}
  		});
  		
  		if (fm.UA.Touch) {
  			autoHide = fm.storage('autoHide') || {};
  			if (typeof autoHide.toolbar === 'undefined') {
  				autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && $.map(options.autoHideUA, function(v){ return fm.UA[v]? true : null; }).length);
  				fm.storage('autoHide', autoHide);
  			}
  			
  			if (autoHide.toolbar) {
  				fm.one('init', function() {
  					fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); });
  				});
  			}
  			
  			fm.bind('load', function() {
  				swipeHandle = $('<div class="elfinder-toolbar-swipe-handle"/>').appendTo(fm.getUI());
  				if (swipeHandle.css('pointer-events') !== 'none') {
  					swipeHandle.remove();
  					swipeHandle = null;
  				}
  			});
  			
  			self.on('toggle', function(e, data) {
  				var wz    = fm.getUI('workzone'),
  					toshow= self.is(':hidden'),
  					wzh   = wz.height(),
  					h     = self.height(),
  					tbh   = self.outerHeight(true),
  					delta = tbh - h,
  					opt   = $.extend({
  						step: function(now) {
  							wz.height(wzh + (toshow? (now + delta) * -1 : h - now));
  							fm.trigger('resize');
  						},
  						always: function() {
  							wz.height(wzh + (toshow? self.outerHeight(true) * -1 : tbh));
  							fm.trigger('resize');
  							if (swipeHandle) {
  								if (toshow) {
  									swipeHandle.stop(true, true).hide();
  								} else {
  									swipeHandle.height(data.handleH? data.handleH : '');
  									fm.resources.blink(swipeHandle, 'slowonce');
  								}
  							}
  							data.init && fm.trigger('uiautohide');
  						}
  					}, data);
  				self.data('swipeClose', ! toshow).animate({height : 'toggle'}, opt);
  				autoHide.toolbar = !toshow;
  				fm.storage('autoHide', $.extend(fm.storage('autoHide'), {toolbar: autoHide.toolbar}));
  			});
  		}
  	});
  	
  
  	
  	return this;
  };
  
  
  /*
   * File: /js/ui/tree.js
   */
  
  /**
   * @class  elFinder folders tree
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfindertree = function(fm, opts) {
  	var treeclass = fm.res('class', 'tree');
  	
  	this.not('.'+treeclass).each(function() {
  
  		var c = 'class', mobile = fm.UA.Mobile,
  			
  			/**
  			 * Root directory class name
  			 *
  			 * @type String
  			 */
  			root      = fm.res(c, 'treeroot'),
  
  			/**
  			 * Open root dir if not opened yet
  			 *
  			 * @type Boolean
  			 */
  			openRoot  = opts.openRootOnLoad,
  
  			/**
  			 * Open current work dir if not opened yet
  			 *
  			 * @type Boolean
  			 */
  			openCwd   = opts.openCwdOnOpen,
  
  			/**
  			 * Subtree class name
  			 *
  			 * @type String
  			 */
  			subtree   = fm.res(c, 'navsubtree'),
  			
  			/**
  			 * Directory class name
  			 *
  			 * @type String
  			 */
  			navdir    = fm.res(c, 'treedir'),
  			
  			/**
  			 * Directory CSS selector
  			 *
  			 * @type String
  			 */
  			selNavdir = 'span.' + navdir,
  			
  			/**
  			 * Collapsed arrow class name
  			 *
  			 * @type String
  			 */
  			collapsed = fm.res(c, 'navcollapse'),
  			
  			/**
  			 * Expanded arrow class name
  			 *
  			 * @type String
  			 */
  			expanded  = fm.res(c, 'navexpand'),
  			
  			/**
  			 * Class name to mark arrow for directory with already loaded children
  			 *
  			 * @type String
  			 */
  			loaded    = 'elfinder-subtree-loaded',
  			
  			/**
  			 * Arraw class name
  			 *
  			 * @type String
  			 */
  			arrow = fm.res(c, 'navarrow'),
  			
  			/**
  			 * Current directory class name
  			 *
  			 * @type String
  			 */
  			active    = fm.res(c, 'active'),
  			
  			/**
  			 * Droppable dirs dropover class
  			 *
  			 * @type String
  			 */
  			dropover = fm.res(c, 'adroppable'),
  			
  			/**
  			 * Hover class name
  			 *
  			 * @type String
  			 */
  			hover    = fm.res(c, 'hover'),
  			
  			/**
  			 * Disabled dir class name
  			 *
  			 * @type String
  			 */
  			disabled = fm.res(c, 'disabled'),
  			
  			/**
  			 * Draggable dir class name
  			 *
  			 * @type String
  			 */
  			draggable = fm.res(c, 'draggable'),
  			
  			/**
  			 * Droppable dir  class name
  			 *
  			 * @type String
  			 */
  			droppable = fm.res(c, 'droppable'),
  			
  			/**
  			 * Un-disabled cmd `paste` volume's root wrapper class
  			 * 
  			 * @type String
  			 */
  			pastable = 'elfinder-navbar-wrapper-pastable',
  			
  			/**
  			 * Un-disabled cmd `upload` volume's root wrapper class
  			 * 
  			 * @type String
  			 */
  			uploadable = 'elfinder-navbar-wrapper-uploadable',
  			
  			insideNavbar = function(x) {
  				var left = navbar.offset().left;
  					
  				return left <= x && x <= left + navbar.width();
  			},
  			
  			drop = fm.droppable.drop,
  			
  			/**
  			 * Droppable options
  			 *
  			 * @type Object
  			 */
  			droppableopts = $.extend(true, {}, fm.droppable, {
  				// show subfolders on dropover
  				over : function(e, ui) {
  					var dst    = $(this),
  						helper = ui.helper,
  						cl     = hover+' '+dropover,
  						hash, status;
  					e.stopPropagation();
  					helper.data('dropover', helper.data('dropover') + 1);
  					dst.data('dropover', true);
  					if (ui.helper.data('namespace') !== fm.namespace || ! insideNavbar(e.clientX) || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  						dst.removeClass(cl);
  						helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  						return;
  					}
  					dst.addClass(hover)
  					if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  						dst.data('expandTimer', setTimeout(function() {
  							dst.children('.'+arrow).click();
  						}, 500));
  					}
  					hash = fm.navId2Hash(dst.attr('id'));
  					dst.data('dropover', hash);
  					$.each(ui.helper.data('files'), function(i, h) {
  						if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) {
  							dst.removeClass(cl);
  							return false; // break $.each
  						}
  					});
  					if (helper.data('locked')) {
  						status = 'elfinder-drag-helper-plus';
  					} else {
  						status = 'elfinder-drag-helper-move';
  						if (e.shiftKey || e.ctrlKey || e.metaKey) {
  							status += ' elfinder-drag-helper-plus';
  						}
  					}
  					dst.hasClass(dropover) && helper.addClass(status);
  					setTimeout(function(){ dst.hasClass(dropover) && helper.addClass(status); }, 20);
  				},
  				out : function(e, ui) {
  					var dst    = $(this),
  						helper = ui.helper;
  					e.stopPropagation();
  					helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  					dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  					dst.removeData('dropover')
  					   .removeClass(hover+' '+dropover);
  				},
  				deactivate : function() {
  					$(this).removeData('dropover')
  					       .removeClass(hover+' '+dropover);
  				},
  				drop : function(e, ui) {
  					insideNavbar(e.clientX) && drop.call(this, e, ui);
  				}
  			}),
  			
  			spinner = $(fm.res('tpl', 'navspinner')),
  			
  			/**
  			 * Directory html template
  			 *
  			 * @type String
  			 */
  			tpl = fm.res('tpl', 'navdir'),
  			
  			/**
  			 * Permissions marker html template
  			 *
  			 * @type String
  			 */
  			ptpl = fm.res('tpl', 'perms'),
  			
  			/**
  			 * Lock marker html template
  			 *
  			 * @type String
  			 */
  			ltpl = fm.res('tpl', 'lock'),
  			
  			/**
  			 * Symlink marker html template
  			 *
  			 * @type String
  			 */
  			stpl = fm.res('tpl', 'symlink'),
  			
  			/**
  			 * Html template replacement methods
  			 *
  			 * @type Object
  			 */
  			replace = {
  				id          : function(dir) { return fm.navHash2Id(dir.hash) },
  				cssclass    : function(dir) {
  					var cname = (dir.phash ? '' : root)+' '+navdir+' '+fm.perms2class(dir);
  					dir.dirs && !dir.link && (cname += ' ' + collapsed);
  					opts.getClass && (cname += ' ' + opts.getClass(dir));
  					dir.csscls && (cname += ' ' + fm.escape(dir.csscls));
  					return cname;
  				},
  				permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; },
  				symlink     : function(dir) { return dir.alias ? stpl : ''; },
  				style       : function(dir) { return dir.icon ? 'style="background:url(\''+fm.escape(dir.icon)+'\') 0 0 no-repeat;background-size:contain;"' : ''; }
  			},
  			
  			/**
  			 * Return html for given dir
  			 *
  			 * @param  Object  directory
  			 * @return String
  			 */
  			itemhtml = function(dir) {
  				dir.name = fm.escape(dir.i18 || dir.name);
  				
  				return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) {
  					return dir[key] || (replace[key] ? replace[key](dir) : '');
  				});
  			},
  			
  			/**
  			 * Return only dirs from files list
  			 *
  			 * @param  Array  files list
  			 * @return Array
  			 */
  			filter = function(files) {
  				return $.map(files||[], function(f) { return f.mime == 'directory' ? f : null });
  			},
  			
  			/**
  			 * Find parent subtree for required directory
  			 *
  			 * @param  String  dir hash
  			 * @return jQuery
  			 */
  			findSubtree = function(hash) {
  				return hash ? $('#'+fm.navHash2Id(hash)).next('.'+subtree) : tree;
  			},
  			
  			/**
  			 * Find directory (wrapper) in required node
  			 * before which we can insert new directory
  			 *
  			 * @param  jQuery  parent directory
  			 * @param  Object  new directory
  			 * @return jQuery
  			 */
  			findSibling = function(subtree, dir) {
  				var node = subtree.children(':first'),
  					info, compare;
  
  				compare = fm.naturalCompare;
  				while (node.length) {
  					info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')));
  					
  					if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')))) 
  					&& compare(dir.name, info.name) < 0) {
  						return node;
  					}
  					node = node.next();
  				}
  				return $('');
  			},
  			
  			/**
  			 * Add new dirs in tree
  			 *
  			 * @param  Array  dirs list
  			 * @return void
  			 */
  			updateTree = function(dirs) {
  				var length  = dirs.length,
  					orphans = [],
  					i = dirs.length,
  					dir, html, parent, sibling, init, atonce = {}, base;
  
  				var firstVol = true; // check for netmount volume
  				while (i--) {
  					dir = dirs[i];
  
  					if ($('#'+fm.navHash2Id(dir.hash)).length) {
  						continue;
  					}
  					
  					if ((parent = findSubtree(dir.phash)).length) {
  						if (dir.phash && ((init = !parent.children().length) || (sibling = findSibling(parent, dir)).length)) {
  							if (init) {
  								if (!atonce[dir.phash]) {
  									atonce[dir.phash] = [];
  								}
  								atonce[dir.phash].push(dir);
  							} else {
  								sibling.before(itemhtml(dir));
  							}
  						} else {
  							parent[firstVol || dir.phash ? 'append' : 'prepend'](itemhtml(dir));
  							firstVol = false;
  							if (!dir.phash) {
  								base = $('#'+fm.navHash2Id(dir.hash)).parent();
  								if (!dir.disabled || dir.disabled.length < 1) {
  									base.addClass(pastable+' '+uploadable);
  								} else {
  									if ($.inArray('paste', dir.disabled) === -1) {
  										base.addClass(pastable);
  									}
  									if ($.inArray('upload', dir.disabled) === -1) {
  										base.addClass(uploadable);
  									}
  								}
  							}
  						}
  					} else {
  						orphans.push(dir);
  					}
  				}
  
  				// When init, html append at once
  				if (Object.keys(atonce).length){
  					$.each(atonce, function(p, dirs){
  						var parent = findSubtree(p),
  						    html   = [];
  						dirs.sort(compare);
  						$.each(dirs, function(i, d){
  							html.push(itemhtml(d));
  						});
  						parent.append(html.join(''));
  					});
  				}
  				
  				if (orphans.length && orphans.length < length) {
  					updateTree(orphans);
  					return;
  				} 
  				
  				if (length && !mobile) {
  					updateDroppable();
  				}
  				
  			},
  			
  			/**
  			 * sort function by dir.name
  			 * 
  			 */
  			compare = function(dir1, dir2) {
  				return fm.naturalCompare(dir1.name, dir2.name);
  			},
  
  			/**
  			 * Auto scroll to cwd
  			 *
  			 * @return void
  			 */
  			autoScroll = function(target) {
  				var self = $(this);
  				self.data('autoScrTm') && clearTimeout(self.data('autoScrTm'));
  				self.data('autoScrTm', setTimeout(function() {
  					var current = $('#'+(target || fm.navHash2Id(fm.cwd().hash)));
  					
  					if (current.length) {
  						var parent = tree.parent().stop(false, true),
  						top = parent.offset().top,
  						treeH = parent.height(),
  						bottom = top + treeH - current.outerHeight(),
  						tgtTop = current.offset().top;
  						
  						if (tgtTop < top || tgtTop > bottom) {
  							parent.animate({ scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3 }, { duration : 'fast' });
  						}
  					}
  				}, 100));
  			},
  			
  			/**
  			 * Mark current directory as active
  			 * If current directory is not in tree - load it and its parents
  			 *
  			 * @param {Boolean} do not expand cwd
  			 * @return void
  			 */
  			sync = function(noCwd, dirs) {
  				var cwd     = fm.cwd(),
  					cwdhash = cwd.hash,
  					current = $('#'+fm.navHash2Id(cwdhash)), 
  					noCwd   = noCwd || false,
  					dirs    = dirs || [],
  					rootNode, dir, link, subs, subsLen, cnt;
  				
  				if (openRoot) {
  					rootNode = $('#'+fm.navHash2Id(fm.root()));
  					rootNode.hasClass(loaded) && rootNode.addClass(expanded).next('.'+subtree).show();
  					openRoot = false;
  				}
  				
  				if (!current.hasClass(active)) {
  					tree.find(selNavdir+'.'+active).removeClass(active);
  					current.addClass(active);
  				}
  
  				if (opts.syncTree || !current.length) {
  					if (current.length) {
  						if (!noCwd) {
  							current.addClass(loaded);
  							if (openCwd && current.hasClass(collapsed)) {
  								current.addClass(expanded).next('.'+subtree).slideDown();
  							}
  						}
  						subs = current.parentsUntil('.'+root).filter('.'+subtree);
  						subsLen = subs.length;
  						cnt = 1;
  						subs.show().prev(selNavdir).addClass(expanded, function(){
  							!noCwd && subsLen == cnt++ && autoScroll();
  						});
  						!subsLen && !noCwd && autoScroll();
  						return;
  					}
  					if (fm.newAPI) {
  						dir = fm.file(cwdhash);
  						if (dir && dir.phash) {
  							link = $('#'+fm.navHash2Id(dir.phash));
  							if (link.length && link.hasClass(loaded)) {
  								fm.lazy(function() {
  									updateTree([dir]);
  									sync(noCwd);
  								});
  								return;
  							}
  						}
  						link  = cwd.root? $('#'+fm.navHash2Id(cwd.root)) : null;
  						if (link) {
  							spinner.insertBefore(link.children('.'+arrow));
  							link.removeClass(collapsed);
  						}
  						fm.request({
  							data : {cmd : 'parents', target : cwdhash},
  							preventFail : true
  						})
  						.done(function(data) {
  							if (fm.api < 2.1) {
  								data.tree = data.tree.concat([cwd]);
  							}
  							dirs = $.merge(dirs, filter(data.tree));
  							updateTree(dirs);
  							updateArrows(dirs, loaded);
  							cwdhash == cwd.hash && fm.visible() && sync(noCwd);
  						})
  						.always(function(data) {
  							if (link) {
  								spinner.remove();
  								link.addClass(collapsed+' '+loaded);
  							}
  						});
  					}
  					
  				}
  			},
  			
  			/**
  			 * Make writable and not root dirs droppable
  			 *
  			 * @return void
  			 */
  			updateDroppable = function(target) {
  				var limit = 100,
  					next;
  				
  				if (!target) {
  					tree.find('div.'+uploadable).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable');
  					target = tree.find('div.'+pastable).find(selNavdir+':not(.'+droppable+',.elfinder-ro,.elfinder-na)');
  				}
  				
  				if (target.length > limit) {
  					next = target.slice(limit);
  					target = target.slice(0, limit);
  				}
  				
  				target.droppable(droppableopts);
  				
  				if (next) {
  					setTimeout(function(){
  						updateDroppable(next);
  					}, 20);
  				}
  			},
  			
  			/**
  			 * Check required folders for subfolders and update arrow classes
  			 *
  			 * @param  Array  folders to check
  			 * @param  String css class 
  			 * @return void
  			 */
  			updateArrows = function(dirs, cls) {
  				var sel = cls == loaded
  						? '.'+collapsed+':not(.'+loaded+')'
  						: ':not(.'+collapsed+')';
  				
  						
  				//tree.find('.'+subtree+':has(*)').prev(':not(.'+collapsed+')').addClass(collapsed)
  
  				$.each(dirs, function(i, dir) {
  					$('#'+fm.navHash2Id(dir.phash)+sel)
  						.filter(function() { return $(this).next('.'+subtree).children().length > 0 })
  						.addClass(cls);
  				})
  			},
  			
  			
  			
  			/**
  			 * Navigation tree
  			 *
  			 * @type JQuery
  			 */
  			tree = $(this).addClass(treeclass)
  				// make dirs draggable and toggle hover class
  				.on('mouseenter mouseleave', selNavdir, function(e) {
  					var link  = $(this), 
  						enter = e.type == 'mouseenter';
  					
  					if (!link.hasClass(dropover+' '+disabled)) {
  						!mobile && enter && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo') && link.draggable(fm.draggable);
  						link.toggleClass(hover, enter);
  					}
  				})
  				// add/remove dropover css class
  				.on('dropover dropout drop', selNavdir, function(e) {
  					$(this)[e.type == 'dropover' ? 'addClass' : 'removeClass'](dropover+' '+hover);
  				})
  				// open dir or open subfolders in tree
  				.on('click', selNavdir, function(e) {
  					var link = $(this),
  						hash = fm.navId2Hash(link.attr('id')),
  						file = fm.file(hash);
  					
  						if (link.data('longtap')) {
  							e.stopPropagation();
  						return;
  					}
  					
  					if (hash != fm.cwd().hash && !link.hasClass(disabled)) {
  						fm.exec('open', hash).done(function() {
  							fm.select({selected: [hash]});
  						});
  					} else {
  						if (link.hasClass(collapsed)) {
  							link.children('.'+arrow).click();
  						}
  						fm.select({selected: [hash]});
  					}
  				})
  				// for touch device
  				.on('touchstart', selNavdir, function(e) {
  					if (e.originalEvent.touches.length > 1) {
  						return;
  					}
  					var evt = e.originalEvent,
  					p = $(this)
  					.addClass(hover)
  					.data('longtap', null)
  					.data('tmlongtap', setTimeout(function(e){
  						// long tap
  						p.data('longtap', true);
  						fm.trigger('contextmenu', {
  							'type'    : 'navbar',
  							'targets' : [fm.navId2Hash(p.attr('id'))],
  							'x'       : evt.touches[0].pageX,
  							'y'       : evt.touches[0].pageY
  						});
  					}, 500));
  				})
  				.on('touchmove touchend', selNavdir, function(e) {
  					clearTimeout($(this).data('tmlongtap'));
  					if (e.type == 'touchmove') {
  						$(this).removeClass(hover);
  					}
  				})
  				// toggle subfolders in tree
  				.on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) {
  					var arrow = $(this),
  						link  = arrow.parent(selNavdir),
  						stree = link.next('.'+subtree),
  						slideTH = 30, cnt;
  
  					e.stopPropagation();
  
  					if (link.hasClass(loaded)) {
  						link.toggleClass(expanded);
  						fm.lazy(function() {
  							cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length;
  							if (cnt > slideTH) {
  								stree.toggle();
  								fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  							} else {
  								stree.stop(true, true).slideToggle('normal', function(){
  									fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  								});
  							}
  						});
  					} else {
  						spinner.insertBefore(arrow);
  						link.removeClass(collapsed);
  
  						fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))})
  							.done(function(data) { 
  								updateTree(filter(data.tree)); 
  								
  								if (stree.children().length) {
  									link.addClass(collapsed+' '+expanded);
  									if (stree.children().length > slideTH) {
  										stree.show();
  										fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  									} else {
  										stree.stop(true, true).slideDown('normal', function(){
  											fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  										});
  									}
  								} 
  								sync(true);
  							})
  							.always(function(data) {
  								spinner.remove();
  								link.addClass(loaded);
  							});
  					}
  				})
  				.on('contextmenu', selNavdir, function(e) {
  					var self = $(this);
  					e.preventDefault();
  
  					fm.trigger('contextmenu', {
  						'type'    : 'navbar',
  						'targets' : [fm.navId2Hash($(this).attr('id'))],
  						'x'       : e.pageX,
  						'y'       : e.pageY
  					});
  					
  					self.addClass('ui-state-hover');
  					
  					fm.getUI('contextmenu').children().on('mouseenter', function() {
  						self.addClass('ui-state-hover');
  					});
  					
  					fm.bind('closecontextmenu', function() {
  						self.removeClass('ui-state-hover');
  					});
  				})
  				.on('scrolltoview', selNavdir, function() {
  					autoScroll($(this).attr('id'));
  				}),
  			// move tree into navbar
  			navbar = fm.getUI('navbar').append(tree).show()
  				
  			;
  
  		fm.open(function(e) {
  			var data = e.data,
  				dirs = filter(data.files),
  				contextmenu = fm.getUI('contextmenu');
  
  			data.init && tree.empty();
  
  			if (fm.UA.iOS) {
  				navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  			}
  
  			if (dirs.length) {
  				fm.lazy(function() {
  					if (!contextmenu.data('cmdMaps')) {
  						contextmenu.data('cmdMaps', {});
  					}
  					updateTree(dirs);
  					updateArrows(dirs, loaded);
  					// support volume driver option `uiCmdMap`
  					$.each(dirs, function(k, v){
  						if (v.volumeid) {
  							if (v.uiCmdMap && Object.keys(v.uiCmdMap).length && !contextmenu.data('cmdMaps')[v.volumeid]) {
  								contextmenu.data('cmdMaps')[v.volumeid] = v.uiCmdMap;
  							}
  						}
  					});
  				});
  			} 
  			sync(false, dirs);
  		})
  		// add new dirs
  		.add(function(e) {
  			var dirs = filter(e.data.added);
  
  			if (dirs.length) {
  				updateTree(dirs);
  				updateArrows(dirs, collapsed);
  			}
  		})
  		// update changed dirs
  		.change(function(e) {
  			var dirs = filter(e.data.changed),
  				length = dirs.length,
  				l    = length,
  				dir, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded;
  			
  			while (l--) {
  				dir = dirs[l];
  				if ((node = $('#'+fm.navHash2Id(dir.hash))).length) {
  					if (dir.phash) {
  						realParent  = node.closest('.'+subtree);
  						reqParent   = findSubtree(dir.phash);
  						realSibling = node.parent().next();
  						reqSibling  = findSibling(reqParent, dir);
  						
  						if (!reqParent.length) {
  							continue;
  						}
  						
  						if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) {
  							reqSibling.length ? reqSibling.before(node) : reqParent.append(node);
  						}
  					}
  					isExpanded = node.hasClass(expanded);
  					isLoaded   = node.hasClass(loaded);
  					tmp        = $(itemhtml(dir));
  					node.replaceWith(tmp.children(selNavdir));
  					
  					if (dir.dirs 
  					&& (isExpanded || isLoaded) 
  					&& (node = $('#'+fm.navHash2Id(dir.hash))) 
  					&& node.next('.'+subtree).children().length) {
  						isExpanded && node.addClass(expanded);
  						isLoaded && node.addClass(loaded);
  					}
  				}
  			}
  
  			sync();
  			length && !mobile && updateDroppable();
  		})
  		// remove dirs
  		.remove(function(e) {
  			var dirs = e.data.removed,
  				l    = dirs.length,
  				node, stree;
  			
  			while (l--) {
  				if ((node = $('#'+fm.navHash2Id(dirs[l]))).length) {
  					stree = node.closest('.'+subtree);
  					node.parent().detach();
  					if (!stree.children().length) {
  						stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded);
  					}
  				}
  			}
  		})
  		// lock/unlock dirs while moving
  		.bind('lockfiles unlockfiles', function(e) {
  			var lock = e.type == 'lockfiles',
  				helperLocked = e.data.helper? e.data.helper.data('locked') : false,
  				act  = (lock && !helperLocked) ? 'disable' : 'enable',
  				dirs = $.map(e.data.files||[], function(h) {  
  					var dir = fm.file(h);
  					return dir && dir.mime == 'directory' ? h : null;
  				});
  				
  			$.each(dirs, function(i, hash) {
  				var dir = $('#'+fm.navHash2Id(hash));
  				
  				if (dir.length && !helperLocked) {
  					dir.hasClass(draggable) && dir.draggable(act);
  					dir.hasClass(droppable) && dir.droppable(act);
  					dir[lock ? 'addClass' : 'removeClass'](disabled);
  				}
  			});
  		});
  
  	});
  	
  	return this;
  };
  
  
  /*
   * File: /js/ui/uploadButton.js
   */
  
  /**
   * @class  elFinder toolbar's button tor upload file
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderuploadbutton = function(cmd) {
  	return this.each(function() {
  		var button = $(this).elfinderbutton(cmd)
  				.off('click'), 
  			form = $('<form/>').appendTo(button),
  			input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>')
  				.change(function() {
  					var _input = $(this);
  					if (_input.val()) {
  						cmd.exec({input : _input.remove()[0]});
  						input.clone(true).appendTo(form);
  					} 
  				})
  				.on('dragover', function(e) {
  					e.originalEvent.dataTransfer.dropEffect = 'copy';
  				});
  
  		form.append(input.clone(true));
  				
  		cmd.change(function() {
  			form[cmd.disabled() ? 'hide' : 'show']();
  		})
  		.change();
  	});
  };
  
  
  /*
   * File: /js/ui/viewbutton.js
   */
  
  /**
   * @class  elFinder toolbar button to switch current directory view.
   *
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderviewbutton = function(cmd) {
  	return this.each(function() {
  		var button = $(this).elfinderbutton(cmd),
  			icon   = button.children('.elfinder-button-icon');
  
  		cmd.change(function() {
  			var icons = cmd.value == 'icons';
  
  			icon.toggleClass('elfinder-button-icon-view-list', icons);
  			cmd.className = icons? 'view-list' : '';
  			cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons');
  			button.attr('title', cmd.title);
  		});
  	});
  };
  
  
  /*
   * File: /js/ui/workzone.js
   */
  
  /**
   * @class elfinderworkzone - elFinder container for nav and current directory
   * @author Dmitry (dio) Levashov
   **/
  $.fn.elfinderworkzone = function(fm) {
  	var cl = 'elfinder-workzone';
  	
  	this.not('.'+cl).each(function() {
  		var wz     = $(this).addClass(cl),
  			wdelta = wz.outerHeight(true) - wz.height(),
  			parent = wz.parent();
  			
  		parent.add(window).on('resize.' + fm.namespace, function() {
  				var height = parent.height();
  
  				parent.children(':visible:not(.'+cl+')').each(function() {
  					var ch = $(this);
  
  					if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') {
  						height -= ch.outerHeight(true);
  					}
  				});
  				
  				wz.height(height - wdelta);
  			});
  	});
  	return this;
  };
  
  
  /*
   * File: /js/commands/archive.js
   */
  
  /**
   * @class  elFinder command "archive"
   * Archive selected files
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.archive = function() {
  	var self  = this,
  		fm    = self.fm,
  		mimes = [],
  		dfrd;
  		
  	this.variants = [];
  	
  	this.disableOnSearch = false;
  	
  	/**
  	 * Update mimes on open/reload
  	 *
  	 * @return void
  	 **/
  	fm.bind('open reload', function() {
  		self.variants = [];
  		$.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) {
  			self.variants.push([mime, fm.mime2kind(mime)])
  		});
  		self.change();
  	});
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length,
  			chk = (cnt && sel[0].phash && (fm.file(sel[0].phash) || {}).write),
  			cwdId;
  		
  		if (chk && fm.searchStatus.state > 1) {
  			cwdId = fm.cwd().volumeid;
  			chk = (cnt === $.map(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? f : null; }).length);
  		}
  		
  		return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1;
  	}
  	
  	this.exec = function(hashes, type) {
  		var files = this.files(hashes),
  			cnt   = files.length,
  			mime  = type || mimes[0],
  			cwd   = fm.file(files[0].phash) || null,
  			error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'],
  			i, open;
  
  		dfrd = $.Deferred().fail(function(error) {
  			error && fm.error(error);
  		});
  
  		if (!(this.enabled() && cnt && mimes.length && $.inArray(mime, mimes) !== -1)) {
  			return dfrd.reject();
  		}
  		
  		if (!cwd.write) {
  			return dfrd.reject(error);
  		}
  		
  		for (i = 0; i < cnt; i++) {
  			if (!files[i].read) {
  				return dfrd.reject(error);
  			}
  		}
  
  		self.mime   = mime;
  		self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : '');
  		self.data   = {targets : self.hashes(hashes), type : mime};
  		
  		if (fm.cwd().hash !== cwd.hash) {
  			open = fm.exec('open', cwd.hash);
  		} else {
  			open = null;
  		}
  		
  		$.when(open).done(function() {
  			fm.selectfiles({files : hashes});
  			dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  		});
  		
  		return dfrd;
  	}
  
  };
  
  
  /*
   * File: /js/commands/back.js
   */
  
  /**
   * @class  elFinder command "back"
   * Open last visited folder
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.back = function() {
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  	this.shortcuts      = [{
  		pattern     : 'ctrl+left backspace'
  	}];
  	
  	this.getstate = function() {
  		return this.fm.history.canBack() ? 0 : -1;
  	}
  	
  	this.exec = function() {
  		return this.fm.history.back();
  	}
  
  };
  
  
  /*
   * File: /js/commands/chmod.js
   */
  
  /**
   * @class elFinder command "chmod".
   * Chmod files.
   *
   * @type  elFinder.command
   * @author Naoki Sawada
   */
  elFinder.prototype.commands.chmod = function() {
  	this.updateOnSelect = false;
  	var fm  = this.fm,
  	level = {
  		0 : 'owner',
  		1 : 'group',
  		2 : 'other'
  	},
  	msg = {
  		read     : fm.i18n('read'),
  		write    : fm.i18n('write'),
  		execute  : fm.i18n('execute'),
  		perm     : fm.i18n('perm'),
  		kind     : fm.i18n('kind'),
  		files    : fm.i18n('files')
  	},
  	isPerm = function(perm){
  		return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i));
  	};
  
  	this.tpl = {
  		main       : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>'
  					+'{dataTable}',
  		itemTitle  : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>',
  		groupTitle : '<strong>{items}: {num}</strong>',
  		dataTable  : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>'
  					+'<div class="">'+msg.perm+': <input id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>',
  		fieldset   : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>'
  					+'<input type="checkbox" value="4" id="{id}-read-{level}-perm"{checked-r}> <label for="{id}-read-{level}-perm">'+msg.read+'</label><br>'
  					+'<input type="checkbox" value="6" id="{id}-write-{level}-perm"{checked-w}> <label for="{id}-write-{level}-perm">'+msg.write+'</label><br>'
  					+'<input type="checkbox" value="5" id="{id}-execute-{level}-perm"{checked-x}> <label for="{id}-execute-{level}-perm">'+msg.execute+'</label><br>'
  	};
  
  	this.shortcuts = [{
  		//pattern     : 'ctrl+p'
  	}];
  
  	this.getstate = function(sel) {
  		var fm = this.fm;
  		sel = sel || fm.selected();
  		if (sel.length == 0) {
  			sel = [ fm.cwd().hash ];
  		}
  		return !this._disabled && this.checkstate(this.files(sel)) ? 0 : -1;
  	};
  	
  	this.checkstate = function(sel) {
  		var cnt = sel.length;
  		if (!cnt) return false;
  		var chk = $.map(sel, function(f) {
  			return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? f : null;
  		}).length;
  		return (cnt == chk)? true : false;
  	};
  
  	this.exec = function(hashes) {
  		var files   = this.files(hashes);
  		if (! files.length) {
  			hashes = [ this.fm.cwd().hash ];
  			files   = this.files(hashes);
  		}
  		var fm  = this.fm,
  		dfrd    = $.Deferred().always(function() {
  			fm.enable();
  		}),
  		tpl     = this.tpl,
  		hashes  = this.hashes(hashes),
  		cnt     = files.length,
  		file    = files[0],
  		id = fm.namespace + '-perm-' + file.hash,
  		view    = tpl.main,
  		checked = ' checked="checked"',
  		buttons = function() {
  			var buttons = {};
  			buttons[fm.i18n('btnApply')] = save;
  			buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  			return buttons;
  		},
  		save = function() {
  			var perm = $.trim($('#'+id+'-perm').val());
  			
  			if (!isPerm(perm)) return false;
  			
  			dialog.elfinderdialog('close');
  			
  			fm.request({
  				data : {
  					cmd     : 'chmod',
  					targets : hashes,
  					mode    : perm
  				},
  				notify : {type : 'chmod', cnt : cnt}
  			})
  			.fail(function(error) {
  				dfrd.reject(error);
  			})
  			.done(function(data) {
  				dfrd.resolve(data);
  			});
  		},
  		setperm = function() {
  			var perm = '';
  			var _perm;
  			for (var i = 0; i < 3; i++){
  				_perm = 0;
  				if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) {
  					_perm = (_perm | 4);
  				}
  				if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) {
  					_perm = (_perm | 2);
  				}
  				if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) {
  					_perm = (_perm | 1);
  				}
  				perm += _perm.toString(8);
  			}
  			$('#'+id+'-perm').val(perm);
  		},
  		setcheck = function(perm) {
  			var _perm;
  			for (var i = 0; i < 3; i++){
  				_perm = parseInt(perm.slice(i, i+1), 8);
  				$("#"+id+"-read-"+level[i]+'-perm').prop("checked", false);
  				$("#"+id+"-write-"+level[i]+'-perm').prop("checked", false);
  				$("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false);
  				if ((_perm & 4) == 4) {
  					$("#"+id+"-read-"+level[i]+'-perm').prop("checked", true);
  				}
  				if ((_perm & 2) == 2) {
  					$("#"+id+"-write-"+level[i]+'-perm').prop("checked", true);
  				}
  				if ((_perm & 1) == 1) {
  					$("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true);
  				}
  			}
  			setperm();
  		},
  		makeperm = function(files) {
  			var perm = '777', ret = '', chk, _chk, _perm;
  			var len = files.length;
  			for (var i2 = 0; i2 < len; i2++) {
  				chk = getPerm(files[i2].perm);;
  				ret = '';
  				for (var i = 0; i < 3; i++){
  					_chk = parseInt(chk.slice(i, i+1), 8);
  					_perm = parseInt(perm.slice(i, i+1), 8);
  					if ((_chk & 4) != 4 && (_perm & 4) == 4) {
  						_perm -= 4;
  					}
  					if ((_chk & 2) != 2 && (_perm & 2) == 2) {
  						_perm -= 2;
  					}
  					if ((_chk & 1) != 1 && (_perm & 1) == 1) {
  						_perm -= 1;
  					}
  					ret += _perm.toString(8);
  				}
  				perm = ret;
  			}
  			return perm;
  		},
  		makeName = function(name) {
  			return name? ':'+name : '';
  		},
  		makeDataTable = function(perm, f) {
  			var _perm, fieldset;
  			var value = '';
  			var dataTable = tpl.dataTable;
  			for (var i = 0; i < 3; i++){
  				_perm = parseInt(perm.slice(i, i+1), 8);
  				value += _perm.toString(8);
  				fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]);
  				dataTable = dataTable.replace('{'+i+'}', fieldset)
  				                     .replace('{checked-r}', ((_perm & 4) == 4)? checked : '')
  				                     .replace('{checked-w}', ((_perm & 2) == 2)? checked : '')
  				                     .replace('{checked-x}', ((_perm & 1) == 1)? checked : '');
  			}
  			dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']);
  			return dataTable;
  		},
  		getPerm = function(perm){
  			if (isNaN(parseInt(perm, 8))) {
  				var mode_array = perm.split('');
  				var a = [];
  
  				for (var i = 0, l = mode_array.length; i < l; i++) {
  					if (i === 0 || i === 3 || i === 6) {
  						if (mode_array[i].match(/[r]/i)) {
  							a.push(1);
  						} else if (mode_array[i].match(/[-]/)) {
  							a.push(0);
  						}
  					} else if ( i === 1 || i === 4 || i === 7) {
  						 if (mode_array[i].match(/[w]/i)) {
  							a.push(1);
  						} else if (mode_array[i].match(/[-]/)) {
  							a.push(0);
  						}
  					} else {
  						if (mode_array[i].match(/[x]/i)) {
  							a.push(1);
  						} else if (mode_array[i].match(/[-]/)) {
  							a.push(0);
  						}
  					}
  				}
  			
  				a.splice(3, 0, ",");
  				a.splice(7, 0, ",");
  
  				var b = a.join("");
  				var b_array = b.split(",");
  				var c = [];
  			
  				for (var j = 0, m = b_array.length; j < m; j++) {
  					var p = parseInt(b_array[j], 2).toString(8);
  					c.push(p)
  				}
  
  				perm = c.join('');
  			} else {
  				perm = parseInt(perm, 8).toString(8);
  			}
  			return perm;
  		},
  		opts    = {
  			title : this.title,
  			width : 'auto',
  			buttons : buttons(),
  			close : function() { $(this).elfinderdialog('destroy'); }
  		},
  		dialog = fm.getUI().find('#'+id),
  		tmb = '', title, dataTable;
  
  		if (dialog.length) {
  			dialog.elfinderdialog('toTop');
  			return $.Deferred().resolve();
  		}
  
  		view  = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime));
  		if (cnt > 1) {
  			title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt);
  		} else {
  			title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
  			tmb = fm.tmb(file);
  		}
  
  		dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {});
  
  		view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id);
  
  		dialog = fm.dialog(view, opts);
  		dialog.attr('id', id);
  
  		// load thumbnail
  		if (tmb) {
  			$('<img/>')
  				.on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  				.attr('src', tmb.url);
  		}
  
  		$('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');});
  		$('#' + id + '-perm').on('keydown', function(e) {
  			var c = e.keyCode;
  			e.stopPropagation();
  			if (c == 13) {
  				save();
  				return;
  			}
  		}).on('focus', function(e){
  			$(this).select();
  		}).on('keyup', function(e) {
  			if ($(this).val().length == 3) {
  				$(this).select();
  				setcheck($(this).val());
  			}
  		});
  		
  		return dfrd;
  	};
  };
  
  
  /*
   * File: /js/commands/colwidth.js
   */
  
  /**
   * @class  elFinder command "colwidth"
   * CWD list table columns width to auto
   *
   * @author Naoki Sawada
   **/
  elFinder.prototype.commands.colwidth = function() {
  	this.alwaysEnabled = true;
  	this.updateOnSelect = false;
  	
  	this.getstate = function() {
  		return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1;
  	}
  	
  	this.exec = function() {
  		this.fm.getUI('cwd').trigger('colwidth');
  	}
  	
  };
  
  /*
   * File: /js/commands/copy.js
   */
  
  /**
   * @class elFinder command "copy".
   * Put files in filemanager clipboard.
   *
   * @type  elFinder.command
   * @author  Dmitry (dio) Levashov
   */
  elFinder.prototype.commands.copy = function() {
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+c ctrl+insert'
  	}];
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  
  		return !this._disabled && cnt && $.map(sel, function(f) { return f.phash && f.read ? f : null  }).length == cnt ? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var fm   = this.fm,
  			dfrd = $.Deferred()
  				.fail(function(error) {
  					fm.error(error);
  				});
  
  		$.each(this.files(hashes), function(i, file) {
  			if (!(file.read && file.phash)) {
  				return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  			}
  		});
  		
  		return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes)));
  	}
  
  };
  
  
  /*
   * File: /js/commands/cut.js
   */
  
  /**
   * @class elFinder command "copy".
   * Put files in filemanager clipboard.
   *
   * @type  elFinder.command
   * @author  Dmitry (dio) Levashov
   */
  elFinder.prototype.commands.cut = function() {
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+x shift+insert'
  	}];
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  		
  		return !this._disabled && cnt && $.map(sel, function(f) { return f.phash && f.read && !f.locked ? f : null  }).length == cnt ? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var fm     = this.fm,
  			dfrd   = $.Deferred()
  				.fail(function(error) {
  					fm.error(error);
  				});
  
  		$.each(this.files(hashes), function(i, file) {
  			if (!(file.read && file.phash) ) {
  				return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  			}
  			if (file.locked) {
  				return !dfrd.reject(['errLocked', file.name]);
  			}
  		});
  		
  		return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true));
  	}
  
  };
  
  
  /*
   * File: /js/commands/download.js
   */
  
  /**
   * @class elFinder command "download". 
   * Download selected files.
   * Only for new api
   *
   * @author Dmitry (dio) Levashov, dio@std42.ru
   **/
  elFinder.prototype.commands.zipdl = function() {};
  elFinder.prototype.commands.download = function() {
  	var self   = this,
  		fm     = this.fm,
  		zipOn  = false,
  		filter = function(hashes) {
  			var czipdl = (fm.api > 2)? fm.command('zipdl') : null,
  				mixed  = false,
  				croot  = '';
  			
  			if (czipdl !== null && fm.searchStatus.state > 1 && fm.searchStatus.target === '') {
  				croot = fm.root(hashes[0]);
  				$.each(hashes, function(i, h) {
  					if (mixed = (croot !== fm.root(h))) {
  						return false;
  					}
  				});
  			}
  			zipOn = (! mixed && czipdl !== null && fm.isCommandEnabled('zipdl', hashes[0]));
  
  			if (mixed) {
  				hashes = $.map(hashes, function(h) {
  					return fm.isCommandEnabled('download', h)? h : null;
  				});
  			} else {
  				if (!fm.isCommandEnabled('download', hashes[0])) {
  					return [];
  				}
  			}
  			
  			return $.map(self.files(hashes), function(f) { return (! f.read || (! zipOn && f.mime == 'directory')) ? null : f; });
  		};
  	
  	this.linkedCmds = ['zipdl'];
  	
  	this.shortcuts = [{
  		pattern     : 'shift+enter'
  	}];
  	
  	this.getstate = function(sel) {
  		var sel    = this.hashes(sel),
  			cnt    = sel.length,
  			maxReq = this.options.maxRequests || 10,
  			czipdl = (fm.api > 2)? fm.command('zipdl') : null,
  			mixed  = false,
  			croot  = '';
  		
  		if (cnt < 1) {
  			return -1;
  		}
  		cnt = filter(sel).length;
  		
  		return  (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1);
  	};
  	
  	fm.bind('contextmenu', function(e){
  		var fm = self.fm,
  			helper = null,
  			targets, file, link,
  			getExtra = function(file) {
  				var link = file.url || fm.url(file.hash);
  				return {
  					icon: 'link',
  					node: $('<a/>')
  						.attr({href: link, target: '_blank', title: fm.i18n('link')})
  						.text(file.name)
  						.on('mousedown click touchstart touchmove touchend contextmenu', function(e){
  							var cm = fm.getUI('contextmenu');
  							e.stopPropagation();
  							// 'mouseEvInternal' for Firefox's bug (maybe)
  							cm.data('mouseEvInternal', true);
  							setTimeout(function(){
  								cm.data('mouseEvInternal', false);
  							}, 500);
  						})
  						.on('dragstart', function(e) {
  							var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  							helper = null;
  							if (dt) {
  								var icon  = function(f) {
  										var mime = f.mime, i, tmb = fm.tmb(f);
  										i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>';
  										if (tmb) {
  											i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  										}
  										return i;
  									};
  								dt.effectAllowed = 'copyLink';
  								if (dt.setDragImage) {
  									helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body));
  									dt.setDragImage(helper.get(0), 50, 47);
  								}
  								if (!fm.UA.IE) {
  									dt.setData('elfinderfrom', window.location.href + file.phash);
  									dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  								}
  							}
  						})
  						.on('dragend', function(e) {
  							helper && helper.remove();
  						})
  				};
  			};
  		self.extra = null;
  		if (e.data) {
  			targets = e.data.targets || [];
  			if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') {
  				if (file.url != '1') {
  					self.extra = getExtra(file);
  				} else {
  					// Get URL ondemand
  					var node;
  					self.extra = {
  						icon: 'link',
  						node: $('<a/>')
  							.attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'})
  							.text(file.name)
  							.on('click touchstart', function(e){
  								if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  									return;
  								}
  								var parent = node.parent();
  								e.stopPropagation();
  								e.preventDefault();
  								parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner');
  								fm.request({
  									data : {cmd : 'url', target : file.hash},
  									preventDefault : true
  								})
  								.always(function(data) {
  									parent.removeClass('elfinder-button-icon-spinner');
  									if (data.url) {
  										var rfile = fm.file(file.hash);
  										rfile.url = data.url;
  										node.replaceWith(getExtra(file).node);
  									} else {
  										parent.addClass('ui-state-disabled');
  									}
  								});
  
  							})
  					};
  					node = self.extra.node;
  					node.ready(function(){
  						setTimeout(function(){
  							node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto');
  						}, 10);
  					});
  				}
  			}
  		}
  	});
  	
  	this.exec = function(hashes) {
  		var hashes  = this.hashes(hashes),
  			fm      = this.fm,
  			base    = fm.options.url,
  			files   = filter(hashes),
  			dfrd    = $.Deferred(),
  			iframes = '',
  			cdata   = '',
  			i, url;
  			
  		if (!files.length) {
  			return dfrd.reject();
  		}
  		
  		var link    = $('<a>').hide().appendTo($('body')),
  			html5dl = (typeof link.get(0).download === 'string');
  		
  		if (zipOn && (files.length > 1 || files[0].mime === 'directory')) {
  			dfrd = fm.request({
  				data : {cmd : 'zipdl', targets : hashes},
  				notify : {type : 'zipdl', cnt : 1, hideCnt : true, multi : true},
  				cancel : true,
  				preventDefault : true
  			}).done(function(e) {
  				var zipdl, dialog, btn = {}, dllink, form,
  					uniq = 'dlw' + (+new Date());
  				if (e.error) {
  					fm.error(e.error);
  					dfrd.reject();
  				} else if (e.zipdl) {
  					zipdl = e.zipdl;
  					if (!html5dl && fm.UA.Mobile) {
  						url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&')
  						+ 'cmd=zipdl&download=1';
  						$.each([hashes[0], zipdl.file, zipdl.name, zipdl.mime], function(key, val) {
  							url += '&targets%5B%5D='+encodeURIComponent(val);
  						});
  						$.each(fm.options.customData, function(key, val) {
  							url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val);
  						});
  						url += '&'+encodeURIComponent(zipdl.name);
  						dllink = $('<a/>')
  							.attr('href', url)
  							.attr('download', encodeURIComponent(zipdl.name))
  							.attr('target', '_blank')
  							.on('click', function() {
  								fm.trigger('download', {files : files});
  								dfrd.resolve(hashes);
  								dialog.elfinderdialog('close');
  							})
  							.append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(zipdl.name));
  						btn[fm.i18n('btnCancel')] = function() {
  							dialog.elfinderdialog('close');
  						};
  						dialog = fm.dialog(dllink, {
  							title: fm.i18n('link'),
  							buttons: btn,
  							width: '200px'
  						});
  					} else {
  						form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>')
  						.append('<input type="hidden" name="cmd" value="zipdl"/>')
  						.append('<input type="hidden" name="download" value="1"/>');
  						$.each([hashes[0], zipdl.file, zipdl.name, zipdl.mime], function(key, val) {
  							form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>');
  						});
  						$.each(fm.options.customData, function(key, val) {
  							form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>');
  						});
  						form.attr('target', uniq).appendTo('body');
  						iframes = $('<iframe style="display:none" name="'+uniq+'">')
  							.appendTo('body')
  							.ready(function() {
  								form.submit().remove();
  								fm.trigger('download', {files : files});
  								dfrd.resolve(hashes);
  								setTimeout(function() {
  									iframes.remove();
  								}, fm.UA.Firefox? 20000 : 1000); // give mozilla 20 sec file to be saved
  							});
  					}
  				}
  			}).fail(function(error) {
  				error && fm.error(error);
  				dfrd.reject();
  			}).always(function() {
  				link.remove();
  			});
  			fm.trigger('download', {files : files});
  			return dfrd;
  		} else {
  			for (i = 0; i < files.length; i++) {
  				url = fm.openUrl(files[i].hash, true);
  				if (html5dl) {
  					link.attr('href', url)
  					.attr('download', encodeURIComponent(files[i].name))
  					.attr('target', '_blank')
  					.get(0).click();
  				} else {
  					if (fm.UA.Mobile) {
  						setTimeout(function(){
  							if (! window.open(url)) {
  								fm.error('errPopup');
  							}
  						}, 100);
  					} else {
  						iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>';
  					}
  				}
  			}
  			link.remove();
  			$(iframes)
  				.appendTo('body')
  				.ready(function() {
  					setTimeout(function() {
  						$(iframes).each(function() {
  							$('#' + $(this).attr('id')).remove();
  						});
  					}, fm.UA.Firefox? (20000 + (10000 * i)) : 1000); // give mozilla 20 sec + 10 sec for each file to be saved
  				});
  			fm.trigger('download', {files : files});
  			return dfrd.resolve(hashes);
  		}
  	};
  
  };
  
  
  /*
   * File: /js/commands/duplicate.js
   */
  
  /**
   * @class elFinder command "duplicate"
   * Create file/folder copy with suffix "copy Number"
   *
   * @type  elFinder.command
   * @author  Dmitry (dio) Levashov
   */
  elFinder.prototype.commands.duplicate = function() {
  	var fm = this.fm;
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  
  		return !this._disabled && cnt && fm.cwd().write && $.map(sel, function(f) { return f.phash && f.read && f.phash === fm.cwd().hash? f : null  }).length == cnt ? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var fm     = this.fm,
  			files  = this.files(hashes),
  			cnt    = files.length,
  			dfrd   = $.Deferred()
  				.fail(function(error) {
  					error && fm.error(error);
  				}), 
  			args = [];
  			
  		if (!cnt || this.getstate(hashes) === -1) {
  			return dfrd.reject();
  		}
  		
  		$.each(files, function(i, file) {
  			if (!file.read || !fm.file(file.phash).write) {
  				return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  			}
  		});
  		
  		if (dfrd.state() == 'rejected') {
  			return dfrd;
  		}
  		
  		return fm.request({
  			data   : {cmd : 'duplicate', targets : this.hashes(hashes)},
  			notify : {type : 'copy', cnt : cnt}
  		});
  		
  	}
  
  };
  
  
  /*
   * File: /js/commands/edit.js
   */
  
  /**
   * @class elFinder command "edit". 
   * Edit text file in dialog window
   *
   * @author Dmitry (dio) Levashov, dio@std42.ru
   **/
  elFinder.prototype.commands.edit = function() {
  	var self  = this,
  		fm    = this.fm,
  		mimes = fm.res('mimes', 'text') || [],
  		rtrim = function(str){
  			return str.replace(/\s+$/, '');
  		},
  	
  		/**
  		 * Return files acceptable to edit
  		 *
  		 * @param  Array  files hashes
  		 * @return Array
  		 **/
  		filter = function(files) {
  			return $.map(files, function(file) {
  				return (file.mime.indexOf('text/') === 0 || $.inArray(file.mime, mimes) !== -1) 
  					&& file.mime.indexOf('text/rtf')
  					&& (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
  					&& file.read && file.write ? file : null;
  			});
  		},
  		
  		/**
  		 * Open dialog with textarea to edit file
  		 *
  		 * @param  String  id       dialog id
  		 * @param  Object  file     file object
  		 * @param  String  content  file content
  		 * @return $.Deferred
  		 **/
  		dialog = function(id, file, content) {
  
  			var dfrd = $.Deferred(),
  				ta   = $('<textarea class="elfinder-file-edit '+fm.res('class', 'editing')+'" rows="20" id="'+id+'-ta">'+fm.escape(content)+'</textarea>'),
  				old  = ta.val(),
  				save = function() {
  					ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  					old = ta.val();
  					dfrd.notifyWith(ta);
  				},
  				cancel = function() {
  					var close = function(){
  						dfrd.reject();
  						ta.elfinderdialog('close');
  					};
  					ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  					if (rtrim(old) !== rtrim(ta.val())) {
  						old = ta.val();
  						fm.confirm({
  							title  : self.title,
  							text   : 'confirmNotSave',
  							accept : {
  								label    : 'btnSaveClose',
  								callback : function() {
  									save();
  									close();
  								}
  							},
  							cancel : {
  								label    : 'btnClose',
  								callback : close
  							}
  						});
  					} else {
  						close();
  					}
  				},
  				savecl = function() {
  					save();
  					cancel();
  				},
  				opts = {
  					title   : fm.escape(file.name),
  					width   : self.options.dialogWidth || 450,
  					buttons : {},
  					btnHoverFocus : false,
  					closeOnEscape : false,
  					close   : function() { 
  						var $this = $(this),
  						close = function(){
  							ta.editor && ta.editor.close(ta[0], ta.editor.instance);
  							$this.elfinderdialog('destroy');
  						};
  						ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  						if (rtrim(old) !== rtrim(ta.val())) {
  							fm.confirm({
  								title  : self.title,
  								text   : 'confirmNotSave',
  								accept : {
  									label    : 'btnSaveClose',
  									callback : function() {
  										save();
  										close();
  									}
  								},
  								cancel : {
  									label    : 'btnClose',
  									callback : close
  								}
  							});
  						} else {
  							close();
  						}
  					},
  					open    : function() { 
  						fm.disable();
  						ta.focus(); 
  						ta[0].setSelectionRange && ta[0].setSelectionRange(0, 0);
  						if (ta.editor) {
  							ta.editor.instance = ta.editor.load(ta[0]) || null;
  							ta.editor.focus(ta[0], ta.editor.instance);
  						}
  					}
  					
  				},
  				mimeMatch = function(fileMime, editorMimes){
  					editorMimes = editorMimes || mimes.concat('text/');
  					if ($.inArray(fileMime, editorMimes) !== -1 ) {
  						return true;
  					}
  					var i, l;
  					l = editorMimes.length;
  					for (i = 0; i < l; i++) {
  						if (fileMime.indexOf(editorMimes[i]) === 0) {
  							return true;
  						}
  					}
  					return false;
  				},
  				extMatch = function(fileName, editorExts){
  					if (!editorExts || !editorExts.length) {
  						return true;
  					}
  					var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  					i, l;
  					l = editorExts.length;
  					for (i = 0; i < l; i++) {
  						if (ext === editorExts[i].toLowerCase()) {
  							return true;
  						}
  					}
  					return false;
  				};
  				
  				ta.getContent = function() {
  					return ta.val();
  				};
  				
  				$.each(self.options.editors || [], function(i, editor) {
  					if (mimeMatch(file.mime, editor.mimes || null)
  					&& extMatch(file.name, editor.exts || null)
  					&& typeof editor.load == 'function'
  					&& typeof editor.save == 'function') {
  						ta.editor = {
  							load     : editor.load,
  							save     : editor.save,
  							close    : typeof editor.close == 'function' ? editor.close : function() {},
  							focus    : typeof editor.focus == 'function' ? editor.focus : function() {},
  							instance : null,
  							doSave   : save,
  							doCancel : cancel,
  							doClose  : savecl,
  							file     : file
  						};
  						
  						return false;
  					}
  				});
  				
  				if (!ta.editor) {
  					ta.keydown(function(e) {
  						var code = e.keyCode,
  							value, start;
  						
  						e.stopPropagation();
  						if (code == 9) {
  							e.preventDefault();
  							// insert tab on tab press
  							if (this.setSelectionRange) {
  								value = this.value;
  								start = this.selectionStart;
  								this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
  								start += 1;
  								this.setSelectionRange(start, start);
  							}
  						}
  						
  						if (e.ctrlKey || e.metaKey) {
  							// close on ctrl+w/q
  							if (code == 81 || code == 87) {
  								e.preventDefault();
  								cancel();
  							}
  							if (code == 83) {
  								e.preventDefault();
  								save();
  							}
  						}
  						
  					}).on('mouseenter', function(){this.focus();});
  				}
  				
  				opts.buttons[fm.i18n('btnSave')]      = save;
  				opts.buttons[fm.i18n('btnSaveClose')] = savecl;
  				opts.buttons[fm.i18n('btnCancel')]    = cancel;
  				
  				fm.dialog(ta, opts)
  					.attr('id', id)
  					.on('keydown keyup keypress', function(e) {
  						e.stopPropagation();
  					});
  				return dfrd.promise();
  		},
  		
  		/**
  		 * Get file content and
  		 * open dialog with textarea to edit file content
  		 *
  		 * @param  String  file hash
  		 * @return jQuery.Deferred
  		 **/
  		edit = function(file, doconv) {
  			var hash   = file.hash,
  				opts   = fm.options,
  				dfrd   = $.Deferred(), 
  				data   = {cmd : 'file', target : hash},
  				id    = 'edit-'+fm.namespace+'-'+file.hash,
  				d = fm.getUI().find('#'+id),
  				conv   = !doconv? 0 : 1,
  				error;
  			
  			
  			if (d.length) {
  				d.elfinderdialog('toTop');
  				return dfrd.resolve();
  			}
  			
  			if (!file.read || !file.write) {
  				error = ['errOpen', file.name, 'errPerm'];
  				fm.error(error);
  				return dfrd.reject(error);
  			}
  			
  			fm.request({
  				data   : {cmd : 'get', target  : hash, conv : conv},
  				notify : {type : 'file', cnt : 1},
  				syncOnFail : true
  			})
  			.done(function(data) {
  				if (data.doconv) {
  					fm.confirm({
  						title  : self.title,
  						text   : 'confirmConvUTF8',
  						accept : {
  							label    : 'btnConv',
  							callback : function() {  
  								dfrd = edit(file, 1);
  							}
  						},
  						cancel : {
  							label    : 'btnCancel',
  							callback : function() { dfrd.reject(); }
  						}
  					});
  				} else {
  					dialog(id, file, data.content)
  						.progress(function() {
  							var ta = this;
  							fm.request({
  								options : {type : 'post'},
  								data : {
  									cmd     : 'put',
  									target  : hash,
  									content : ta.getContent()
  								},
  								notify : {type : 'save', cnt : 1},
  								syncOnFail : true
  							})
  							.fail(function(error) {
  								dfrd.reject(error);
  							})
  							.done(function(data) {
  								data.changed && data.changed.length && fm.change(data);
  								dfrd.resolve(data);
  								setTimeout(function(){
  									ta.focus();
  									ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
  								}, 50);
  							});
  						});
  				}
  			})
  			.fail(function(error) {
  				dfrd.reject(error);
  			});
  
  			return dfrd.promise();
  		};
  	
  	
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+e'
  	}];
  	
  	this.init = function() {
  		this.onlyMimes = this.options.mimes || [];
  	};
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  
  		return !this._disabled && cnt && filter(sel).length == cnt ? 0 : -1;
  	};
  	
  	this.exec = function(hashes) {
  		var files = filter(this.files(hashes)),
  			list  = [],
  			file;
  
  		if (this.disabled()) {
  			return $.Deferred().reject();
  		}
  
  		while ((file = files.shift())) {
  			list.push(edit(file));
  		}
  		
  		return list.length 
  			? $.when.apply(null, list)
  			: $.Deferred().reject();
  	};
  
  };
  
  
  /*
   * File: /js/commands/extract.js
   */
  
  /**
   * @class  elFinder command "extract"
   * Extract files from archive
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.extract = function() {
  	var self    = this,
  		fm      = self.fm,
  		mimes   = [],
  		filter  = function(files) {
  			return $.map(files, function(file) { 
  				return file.read && $.inArray(file.mime, mimes) !== -1 ? file : null
  				
  			})
  		};
  	
  	this.variants = [];
  	this.disableOnSearch = true;
  	
  	// Update mimes list on open/reload
  	fm.bind('open reload', function() {
  		mimes = fm.option('archivers')['extract'] || [];
  		if (fm.api > 2) {
  			self.variants = [['makedir', fm.i18n('cmdmkdir')], ['intohere', fm.i18n('btnCwd')]];
  		} else {
  			self.variants = [['intohere', fm.i18n('btnCwd')]];
  		}
  		self.change();
  	});
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  		
  		return !this._disabled && cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1;
  	}
  	
  	this.exec = function(hashes, extractTo) {
  		var files    = this.files(hashes),
  			dfrd     = $.Deferred(),
  			cnt      = files.length,
  			makedir  = (extractTo == 'makedir')? 1 : 0,
  			i, error,
  			decision;
  
  		var overwriteAll = false;
  		var omitAll = false;
  		var mkdirAll = 0;
  
  		var names = $.map(fm.files(hashes), function(file) { return file.name; });
  		var map = {};
  		$.map(fm.files(hashes), function(file) { map[file.name] = file; });
  		
  		var decide = function(decision) {
  			switch (decision) {
  				case 'overwrite_all' :
  					overwriteAll = true;
  					break;
  				case 'omit_all':
  					omitAll = true;
  					break;
  			}
  		};
  
  		var unpack = function(file) {
  			if (!(file.read && fm.file(file.phash).write)) {
  				error = ['errExtract', file.name, 'errPerm'];
  				fm.error(error);
  				dfrd.reject(error);
  			} else if ($.inArray(file.mime, mimes) === -1) {
  				error = ['errExtract', file.name, 'errNoArchive'];
  				fm.error(error);
  				dfrd.reject(error);
  			} else {
  				fm.request({
  					data:{cmd:'extract', target:file.hash, makedir:makedir},
  					notify:{type:'extract', cnt:1},
  					syncOnFail:true
  				})
  				.fail(function (error) {
  					if (dfrd.state() != 'rejected') {
  						dfrd.reject(error);
  					}
  				})
  				.done(function () {
  				});
  			}
  		};
  		
  		var confirm = function(files, index) {
  			var file = files[index],
  			name = file.name.replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, ''),
  			existed = ($.inArray(name, names) >= 0),
  			next = function(){
  				if((index+1) < cnt) {
  					confirm(files, index+1);
  				} else {
  					dfrd.resolve();
  				}
  			};
  			if (!makedir && existed && map[name].mime != 'directory') {
  				fm.confirm(
  					{
  						title : fm.i18n('ntfextract'),
  						text  : ['errExists', name, 'confirmRepl'],
  						accept:{
  							label : 'btnYes',
  							callback:function (all) {
  								decision = all ? 'overwrite_all' : 'overwrite';
  								decide(decision);
  								if(!overwriteAll && !omitAll) {
  									if('overwrite' == decision) {
  										unpack(file);
  									}
  									if((index+1) < cnt) {
  										confirm(files, index+1);
  									} else {
  										dfrd.resolve();
  									}
  								} else if(overwriteAll) {
  									for (i = index; i < cnt; i++) {
  										unpack(files[i]);
  									}
  									dfrd.resolve();
  								}
  							}
  						},
  						reject : {
  							label : 'btnNo',
  							callback:function (all) {
  								decision = all ? 'omit_all' : 'omit';
  								decide(decision);
  								if(!overwriteAll && !omitAll && (index+1) < cnt) {
  									confirm(files, index+1);
  								} else if (omitAll) {
  									dfrd.resolve();
  								}
  							}
  						},
  						cancel : {
  							label : 'btnCancel',
  							callback:function () {
  								dfrd.resolve();
  							}
  						},
  						all : ((index+1) < cnt)
  					}
  				);
  			} else if (!makedir) {
  				if (mkdirAll == 0) {
  					fm.confirm({
  						title : fm.i18n('cmdextract'),
  						text  : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'],
  						accept:{
  							label : 'btnYes',
  							callback:function (all) {
  								all && (mkdirAll = 1);
  								unpack(file);
  								next();
  							}
  						},
  						reject : {
  							label : 'btnNo',
  							callback:function (all) {
  								all && (mkdirAll = -1);
  								next();
  							}
  						},
  						cancel : {
  							label : 'btnCancel',
  							callback:function () {
  								dfrd.resolve();
  							}
  						},
  						all : ((index+1) < cnt)
  					});
  				} else {
  					(mkdirAll > 0) && unpack(file);
  					next();
  				}
  			} else {
  				unpack(file);
  				next();
  			}
  		};
  		
  		if (!(this.enabled() && cnt && mimes.length)) {
  			return dfrd.reject();
  		}
  		
  		if(cnt > 0) {
  			confirm(files, 0);
  		}
  
  		return dfrd;
  	}
  
  };
  
  
  /*
   * File: /js/commands/forward.js
   */
  
  /**
   * @class  elFinder command "forward"
   * Open next visited folder
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.forward = function() {
  	this.alwaysEnabled = true;
  	this.updateOnSelect = true;
  	this.shortcuts = [{
  		pattern     : 'ctrl+right'
  	}];
  	
  	this.getstate = function() {
  		return this.fm.history.canForward() ? 0 : -1;
  	}
  	
  	this.exec = function() {
  		return this.fm.history.forward();
  	}
  	
  };
  
  
  /*
   * File: /js/commands/getfile.js
   */
  
  /**
   * @class elFinder command "getfile". 
   * Return selected files info into outer callback.
   * For use elFinder with wysiwyg editors etc.
   *
   * @author Dmitry (dio) Levashov, dio@std42.ru
   **/
  elFinder.prototype.commands.getfile = function() {
  	var self   = this,
  		fm     = this.fm,
  		filter = function(files) {
  			var o = self.options;
  
  			files = $.map(files, function(file) {
  				return file.mime != 'directory' || o.folders ? file : null;
  			});
  
  			return o.multiple || files.length == 1 ? files : [];
  		};
  	
  	this.alwaysEnabled = true;
  	this.callback      = fm.options.getFileCallback;
  	this._disabled     = typeof(this.callback) == 'function';
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  			
  		return this.callback && cnt && filter(sel).length == cnt ? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var fm    = this.fm,
  			opts  = this.options,
  			files = this.files(hashes),
  			cnt   = files.length,
  			url   = fm.option('url'),
  			tmb   = fm.option('tmbUrl'),
  			dfrd  = $.Deferred()
  				.done(function(data) {
  					fm.trigger('getfile', {files : data});
  					self.callback(data, fm);
  					
  					if (opts.oncomplete == 'close') {
  						fm.hide();
  					} else if (opts.oncomplete == 'destroy') {
  						fm.destroy();
  					}
  				}),
  			result = function(file) {
  				return opts.onlyURL
  					? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url
  					: opts.multiple ? files : files[0];
  			},
  			req = [], 
  			i, file, dim;
  
  		if (this.getstate(hashes) == -1) {
  			return dfrd.reject();
  		}
  			
  		for (i = 0; i < cnt; i++) {
  			file = files[i];
  			if (file.mime == 'directory' && !opts.folders) {
  				return dfrd.reject();
  			}
  			file.baseUrl = url;
  			if (file.url == '1') {
  				req.push(fm.request({
  					data : {cmd : 'url', target : file.hash},
  					notify : {type : 'url', cnt : 1, hideCnt : true},
  					preventDefault : true
  				})
  				.done(function(data) {
  					if (data.url) {
  						var rfile = fm.file(this.hash);
  						rfile.url = this.url = data.url;
  					}
  				}.bind(file)));
  			} else {
  				file.url = fm.url(file.hash);
  			}
  			file.path    = fm.path(file.hash);
  			if (file.tmb && file.tmb != 1) {
  				file.tmb = tmb + file.tmb;
  			}
  			if (!file.width && !file.height) {
  				if (file.dim) {
  					dim = file.dim.split('x');
  					file.width = dim[0];
  					file.height = dim[1];
  				} else if (opts.getImgSize && file.mime.indexOf('image') !== -1) {
  					req.push(fm.request({
  						data : {cmd : 'dim', target : file.hash},
  						notify : {type : 'dim', cnt : 1, hideCnt : true},
  						preventDefault : true
  					})
  					.done(function(data) {
  						if (data.dim) {
  							var dim = data.dim.split('x');
  							var rfile = fm.file(this.hash);
  							rfile.width = this.width = dim[0];
  							rfile.height = this.height = dim[1];
  						}
  					}.bind(file)));
  				}
  			}
  		}
  		
  		if (req.length) {
  			$.when.apply(null, req).always(function() {
  				dfrd.resolve(result(files));
  			})
  			return dfrd;
  		}
  		
  		return dfrd.resolve(result(files));
  	}
  
  };
  
  
  /*
   * File: /js/commands/help.js
   */
  
  /**
   * @class  elFinder command "help"
   * "About" dialog
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.help = function() {
  	var fm   = this.fm,
  		self = this,
  		linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>',
  		linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>',
  		atpl    = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>',
  		url     = /\{url\}/,
  		link    = /\{link\}/,
  		author  = /\{author\}/,
  		work    = /\{work\}/,
  		r       = 'replace',
  		prim    = 'ui-priority-primary',
  		sec     = 'ui-priority-secondary',
  		lic     = 'elfinder-help-license',
  		tab     = '<li class="ui-state-default ui-corner-top elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}">{title}</a></li>',
  		html    = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">', 
  				'<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">'],
  		stpl    = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
  		sep     = '<div class="elfinder-help-separator"/>',
  		
  		
  		about = function() {
  			html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>');
  			html.push('<h3>elFinder</h3>');
  			html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
  			html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+', '+fm.i18n('protocolver')+': <span id="apiver"></span></div>');
  			html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>');
  
  			html.push(sep);
  			
  			html.push(linktpltgt[r](url, 'http://elfinder.org/')[r](link, fm.i18n('homepage')));
  			html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs')));
  			html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github')));
  			html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter')));
  			
  			html.push(sep);
  			
  			html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
  			
  			html.push(atpl[r](author, 'Dmitry "dio" Levashov &lt;dio@std42.ru&gt;')[r](work, fm.i18n('chiefdev')));
  			html.push(atpl[r](author, 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;')[r](work, fm.i18n('maintainer')));
  			html.push(atpl[r](author, 'Alexey Sukhotin &lt;strogg@yandex.ru&gt;')[r](work, fm.i18n('contributor')));
  			html.push(atpl[r](author, 'Naoki Sawada &lt;hypweb@gmail.com&gt;')[r](work, fm.i18n('contributor')));
  			
  			fm.i18[fm.lang].translator && html.push(atpl[r](author, fm.i18[fm.lang].translator)[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
  			
  			html.push(sep);
  			html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a></div>');
  			
  			html.push(sep);
  			html.push('<div class="'+lic+'">Licence: BSD Licence</div>');
  			html.push('<div class="'+lic+'">Copyright © 2009-2016, Studio 42</div>');
  			html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
  			html.push('</div>');
  		},
  		shortcuts = function() {
  			var sh = fm.shortcuts();
  			// shortcuts tab
  			html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  			
  			if (sh.length) {
  				html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
  				$.each(sh, function(i, s) {
  					html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
  				});
  			
  				html.push('</div>');
  			} else {
  				html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
  			}
  			
  			
  			html.push('</div>');
  			
  		},
  		help = function() {
  			// help tab
  			html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  			html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>');
  			html.push('</div>');
  			// end help
  		},
  		debug = function() {
  			// debug tab
  			html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  			html.push('<div class="ui-widget-content elfinder-help-debug"></div>');
  			html.push('</div>');
  			// end debug
  		},
  		debugRender = function() {
  			var render = function(elm, obj) {
  				$.each(obj, function(k, v) {
  					elm.append($('<dt/>').text(k));
  					if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) {
  						elm.append( $('<dd/>').append(render($('<dl/>'), v)));
  					} else {
  						elm.append($('<dd/>').append($('<span/>').text((typeof v === 'object')? '[]' : (v? v : '""'))));
  					}
  				});
  				return elm;
  			},
  			target = content.find('#'+fm.namespace+'-help-debug').find('div:first').empty(),
  			info;
  			
  			if (self.debug.options) {
  				info = $('<fieldset>').append($('<legend/>').text('options'), render($('<dl/>'), self.debug.options));
  				target.append(info);
  			}
  			if (self.debug.debug) {
  				info = $('<fieldset>').append($('<legend/>').text('debug'), render($('<dl/>'), self.debug.debug));
  				target.append(info);
  			}
  		},
  		content = '';
  	
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  	this.state = 0;
  	
  	this.shortcuts = [{
  		pattern     : 'f1',
  		description : this.title
  	}];
  	
  	fm.one('load', function() {
  		var parts = self.options.view || ['about', 'shortcuts', 'help', 'debug'];
  		
  		$.each(parts, function(i, title) {
  			html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
  		});
  		
  		html.push('</ul>');
  
  		$.inArray('about', parts) !== -1 && about();
  		$.inArray('shortcuts', parts) !== -1 && shortcuts();
  		$.inArray('help', parts) !== -1 && help();
  		$.inArray('debug', parts) !== -1 && debug();
  		
  		html.push('</div>');
  		content = $(html.join(''));
  		
  		content.find('.ui-tabs-nav li')
  			.hover(function() {
  				$(this).toggleClass('ui-state-hover');
  			})
  			.children()
  			.click(function(e) {
  				var link = $(this);
  				
  				e.preventDefault();
  				e.stopPropagation();
  				
  				if (!link.hasClass('ui-tabs-selected')) {
  					link.parent().addClass('ui-tabs-selected ui-state-active').siblings().removeClass('ui-tabs-selected').removeClass('ui-state-active');
  					content.find('.ui-tabs-panel').hide().filter(link.attr('href')).show();
  				}
  				
  			})
  			.filter(':first').click();
  
  		self.debug = {};
  
  		fm.bind('open', function(e) {
  			var tabDebug = content.find('.elfinder-help-tab-debug');
  			if (e.data && e.data.debug) {
  				tabDebug.show();
  				self.debug = { options : e.data.options, debug : e.data.debug };
  				if (self.dialog && self.dialog.is(':visible')) {
  					debugRender();
  				}
  			} else {
  				tabDebug.hide();
  			}
  		});
  	});
  	
  	this.getstate = function() {
  		return 0;
  	};
  	
  	this.exec = function() {
  		if (!this.dialog) {
  			content.find('#apiver').text(this.fm.api);
  			this.dialog = this.fm.dialog(content, {title : this.title, width : 530, autoOpen : false, destroyOnClose : false});
  		}
  
  		debugRender();
  
  		this.dialog.elfinderdialog('open').find('.ui-tabs-nav li a:first').click();
  	};
  
  };
  
  
  /*
   * File: /js/commands/home.js
   */
  
  
  elFinder.prototype.commands.home = function() {
  	this.title = 'Home';
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  	this.shortcuts = [{
  		pattern     : 'ctrl+home ctrl+shift+up',
  		description : 'Home'
  	}];
  	
  	this.getstate = function() {
  		var root = this.fm.root(),
  			cwd  = this.fm.cwd().hash;
  			
  		return root && cwd && root != cwd ? 0: -1;
  	}
  	
  	this.exec = function() {
  		return this.fm.exec('open', this.fm.root());
  	}
  	
  
  };
  
  
  /*
   * File: /js/commands/info.js
   */
  
  /**
   * @class elFinder command "info". 
   * Display dialog with file properties.
   *
   * @author Dmitry (dio) Levashov, dio@std42.ru
   **/
  elFinder.prototype.commands.info = function() {
  	var m   = 'msg',
  		fm  = this.fm,
  		spclass = 'elfinder-info-spinner',
  		btnclass = 'elfinder-info-button',
  		msg = {
  			calc     : fm.i18n('calc'),
  			size     : fm.i18n('size'),
  			unknown  : fm.i18n('unknown'),
  			path     : fm.i18n('path'),
  			aliasfor : fm.i18n('aliasfor'),
  			modify   : fm.i18n('modify'),
  			perms    : fm.i18n('perms'),
  			locked   : fm.i18n('locked'),
  			dim      : fm.i18n('dim'),
  			kind     : fm.i18n('kind'),
  			files    : fm.i18n('files'),
  			folders  : fm.i18n('folders'),
  			items    : fm.i18n('items'),
  			yes      : fm.i18n('yes'),
  			no       : fm.i18n('no'),
  			link     : fm.i18n('link'),
  			owner    : fm.i18n('owner'),
  			group    : fm.i18n('group'),
  			perm     : fm.i18n('perm'),
  			getlink  : fm.i18n('getLink')
  		};
  		
  	this.tpl = {
  		main       : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div><table class="elfinder-info-tb">{content}</table>',
  		itemTitle  : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
  		groupTitle : '<strong>{items}: {num}</strong>',
  		row        : '<tr><td>{label} : </td><td>{value}</td></tr>',
  		spinner    : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>'
  	};
  	
  	this.alwaysEnabled = true;
  	this.updateOnSelect = false;
  	this.shortcuts = [{
  		pattern     : 'ctrl+i'
  	}];
  	
  	this.init = function() {
  		$.each(msg, function(k, v) {
  			msg[k] = fm.i18n(v);
  		});
  	};
  	
  	this.getstate = function() {
  		return 0;
  	};
  	
  	this.exec = function(hashes) {
  		var files   = this.files(hashes);
  		if (! files.length) {
  			files   = this.files([ this.fm.cwd().hash ]);
  		}
  		var self    = this,
  			fm      = this.fm,
  			o       = this.options,
  			tpl     = this.tpl,
  			row     = tpl.row,
  			cnt     = files.length,
  			content = [],
  			view    = tpl.main,
  			l       = '{label}',
  			v       = '{value}',
  			reqs    = [],
  			opts    = {
  				title : this.title,
  				width : 'auto',
  				close : function() {
  					$(this).elfinderdialog('destroy');
  					$.each(reqs, function(i, req) {
  						var xhr = (req && req.xhr)? req.xhr : null;
  						if (xhr && xhr.state() == 'pending') {
  							xhr.quiet = true;
  							xhr.abort();
  						}
  					});
  				}
  			},
  			count = [],
  			replSpinner = function(msg, name) { dialog.find('.'+spclass+'-'+name).parent().html(msg); },
  			id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'),
  			dialog = fm.getUI().find('#'+id),
  			customActions = [],
  			size, tmb, file, title, dcnt;
  			
  		if (!cnt) {
  			return $.Deferred().reject();
  		}
  			
  		if (dialog.length) {
  			dialog.elfinderdialog('toTop');
  			return $.Deferred().resolve();
  		}
  		
  			
  		if (cnt == 1) {
  			file  = files[0];
  			
  			view  = view.replace('{class}', fm.mime2class(file.mime));
  			title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>');
  
  			tmb = fm.tmb(file);
  			
  			if (!file.read) {
  				size = msg.unknown;
  			} else if (file.mime != 'directory' || file.alias) {
  				size = fm.formatSize(file.size);
  			} else {
  				size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size');
  				count.push(file.hash);
  			}
  			
  			content.push(row.replace(l, msg.size).replace(v, size));
  			file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
  			content.push(row.replace(l, msg.path).replace(v, fm.escape(fm.path(file.hash, true))));
  			if (file.read) {
  				var href,
  				name_esc = fm.escape(file.name);
  				if (file.url == '1') {
  					content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>'));
  				} else {
  					if (o.nullUrlDirLinkSelf && file.mime == 'directory' && file.url === null) {
  						var loc = window.location;
  						href = loc.pathname + loc.search + '#elf_' + file.hash;
  					} else {
  						href = fm.url(file.hash);
  					}
  					content.push(row.replace(l, msg.link).replace(v,  '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));
  				}
  			}
  			
  			if (file.dim) { // old api
  				content.push(row.replace(l, msg.dim).replace(v, file.dim));
  			} else if (file.mime.indexOf('image') !== -1) {
  				if (file.width && file.height) {
  					content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height));
  				} else {
  					content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim')));
  					reqs.push(fm.request({
  						data : {cmd : 'dim', target : file.hash},
  						preventDefault : true
  					})
  					.fail(function() {
  						replSpinner(msg.unknown, 'dim');
  					})
  					.done(function(data) {
  						replSpinner(data.dim || msg.unknown, 'dim');
  						if (data.dim) {
  							var dim = data.dim.split('x');
  							var rfile = fm.file(file.hash);
  							rfile.width = dim[0];
  							rfile.height = dim[1];
  						}
  					}));
  				}
  			}
  			
  			
  			content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
  			content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
  			content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
  			file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner));
  			file.group && content.push(row.replace(l, msg.group).replace(v, file.group));
  			file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm)));
  			
  			// Add custom info fields
  			if (o.custom) {
  				$.each(o.custom, function(name, details) {
  					if (
  					  (!details.mimes || $.map(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : null;}).length)
  					    &&
  					  (!details.hashRegex || file.hash.match(details.hashRegex))
  					) {
  						// Add to the content
  						content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id)));
  						// Register the action
  						if (details.action && (typeof details.action == 'function')) {
  							customActions.push(details.action);
  						}
  					}
  				});
  			}
  		} else {
  			view  = view.replace('{class}', 'elfinder-cwd-icon-group');
  			title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
  			dcnt  = $.map(files, function(f) { return f.mime == 'directory' ? 1 : null ; }).length;
  			if (!dcnt) {
  				size = 0;
  				$.each(files, function(h, f) { 
  					var s = parseInt(f.size);
  					
  					if (s >= 0 && size >= 0) {
  						size += s;
  					} else {
  						size = 'unknown';
  					}
  				});
  				content.push(row.replace(l, msg.kind).replace(v, msg.files));
  				content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
  			} else {
  				content.push(row.replace(l, msg.kind).replace(v, dcnt == cnt ? msg.folders : msg.folders+' '+dcnt+', '+msg.files+' '+(cnt-dcnt)));
  				content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size')));
  				count = $.map(files, function(f) { return f.hash; });
  				
  			}
  		}
  		
  		view = view.replace('{title}', title).replace('{content}', content.join(''));
  		
  		dialog = fm.dialog(view, opts);
  		dialog.attr('id', id);
  
  		if (file && file.url == '1') {
  			dialog.on('click', '.'+spclass+'-url', function(){
  				$(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url'));
  				fm.request({
  					data : {cmd : 'url', target : file.hash},
  					preventDefault : true
  				})
  				.fail(function() {
  					replSpinner(name_esc, 'url');
  				})
  				.done(function(data) {
  					if (data.url) {
  						replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url');
  						var rfile = fm.file(file.hash);
  						rfile.url = data.url;
  					} else {
  						replSpinner(name_esc, 'url');
  					}
  				});
  			});
  		}
  
  		// load thumbnail
  		if (tmb) {
  			$('<img/>')
  				.on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  				.attr('src', tmb.url);
  		}
  		
  		// send request to count total size
  		if (count.length) {
  			reqs.push(fm.request({
  					data : {cmd : 'size', targets : count},
  					preventDefault : true
  				})
  				.fail(function() {
  					replSpinner(msg.unknown, 'size');
  				})
  				.done(function(data) {
  					var size = parseInt(data.size);
  					replSpinner(size >= 0 ? fm.formatSize(size) : msg.unknown, 'size');
  				})
  			);
  		}
  		
  		// call custom actions
  		if (customActions.length) {
  			$.each(customActions, function(i, action) {
  				try {
  					action(file, fm, dialog);
  				} catch(e) {
  					fm.debug('error', e);
  				}
  			});
  		}
  
  	};
  	
  };
  
  
  /*
   * File: /js/commands/mkdir.js
   */
  
  /**
   * @class  elFinder command "mkdir"
   * Create new folder
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.mkdir = function() {
  	var fm   = this.fm,
  		self = this;
  	
  	this.value           = '';
  	this.disableOnSearch = true;
  	this.updateOnSelect  = false;
  	this.mime            = 'directory';
  	this.prefix          = 'untitled folder';
  	this.exec            = function(contextSel) {
  		if (! contextSel && ! this.options.intoNewFolderToolbtn) {
  			fm.getUI('cwd').trigger('unselectall');
  		}
  		this.move = fm.selected().length? true : false;
  		return $.proxy(fm.res('mixin', 'make'), self)();
  	}
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+shift+n'
  	}];
  
  	this.init = function() {
  		if (this.options.intoNewFolderToolbtn) {
  			this.options.ui = 'mkdirbutton';
  		}
  	}
  	
  	fm.bind('select', function(e) {
  		var sel = (e.data && e.data.selected)? e.data.selected : [];
  		self.title = sel.length? fm.i18n('cmdmkdirin') : fm.i18n('cmdmkdir');
  		self.update(void(0), self.title);
  	});
  	
  	this.getstate = function(sel) {
  		var cwd = fm.cwd(),
  			sel = (sel && sel[0] != cwd.hash)? this.files(sel) : [],
  			cnt = sel.length;
  
  		return !this._disabled && cwd.write && (!cnt || $.map(sel, function(f) { return f.phash && f.read && !f.locked ? f : null  }).length == cnt)? 0 : -1;
  	}
  
  };
  
  
  /*
   * File: /js/commands/mkfile.js
   */
  
  /**
   * @class  elFinder command "mkfile"
   * Create new empty file
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.mkfile = function() {
  	this.disableOnSearch = true;
  	this.updateOnSelect  = false;
  	this.mime            = 'text/plain';
  	this.prefix          = 'untitled file.txt';
  	this.exec            = $.proxy(this.fm.res('mixin', 'make'), this);
  	
  	this.getstate = function() {
  		return !this._disabled && this.fm.cwd().write ? 0 : -1;
  	}
  
  };
  
  
  /*
   * File: /js/commands/netmount.js
   */
  
  /**
   * @class  elFinder command "netmount"
   * Mount network volume with user credentials.
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.netmount = function() {
  	var self = this;
  
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  
  	this.drivers = [];
  	
  	this.handlers = {
  		load : function() {
  			this.drivers = this.fm.netDrivers;
  		}
  	}
  
  	this.getstate = function() {
  		return this.drivers.length ? 0 : -1;
  	}
  	
  	this.exec = function() {
  		var fm = self.fm,
  			dfrd = $.Deferred(),
  			o = self.options,
  			create = function() {
  				var inputs = {
  						protocol : $('<select/>').change(function(e, data){
  							var protocol = this.value;
  							content.find('.elfinder-netmount-tr').hide();
  							content.find('.elfinder-netmount-tr-'+protocol).show();
  							if (typeof o[protocol].select == 'function') {
  								o[protocol].select(fm, e, data);
  							}
  						}).addClass('ui-corner-all')
  					},
  					opts = {
  						title          : fm.i18n('netMountDialogTitle'),
  						resizable      : false,
  						modal          : true,
  						destroyOnClose : true,
  						close          : function() { 
  							//delete self.dialog; 
  							dfrd.state() == 'pending' && dfrd.reject();
  						},
  						buttons        : {}
  					},
  					content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>'),
  					hidden  = $('<div/>'),
  					dialog;
  
  				content.append($('<tr/>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td/>').append(inputs.protocol)));
  
  				$.each(self.drivers, function(i, protocol) {
  					inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
  					$.each(o[protocol].inputs, function(name, input) {
  						input.attr('name', name);
  						if (input.attr('type') != 'hidden') {
  							input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
  							content.append($('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input)));
  						} else {
  							input.addClass('elfinder-netmount-inputs-'+protocol);
  							hidden.append(input);
  						}
  					});
  					o[protocol].protocol = inputs.protocol;
  				});
  				
  				content.append(hidden);
  				
  				content.find('.elfinder-netmount-tr').hide();
  
  				opts.buttons[fm.i18n('btnMount')] = function() {
  					var protocol = inputs.protocol.val(),
  						data = {cmd : 'netmount', protocol: protocol},
  						cur = o[protocol];
  					$.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
  						var val;
  						if (typeof input.val == 'function') {
  							val = $.trim(input.val());
  						} else {
  							val = $.trim(input.value);
  						}
  						if (val) {
  							data[input.name] = val;
  						}
  					});
  
  					if (!data.host) {
  						return fm.trigger('error', {error : 'errNetMountHostReq'});
  					}
  
  					fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
  						.done(function(data) {
  							data.added && data.added.length && fm.exec('open', data.added[0].hash);
  							dfrd.resolve();
  						})
  						.fail(function(error) {
  							//self.dialog.elfinderdialog('open');
  							if (cur.fail && typeof cur.fail == 'function') {
  								cur.fail(fm, error);
  							}
  							dfrd.reject(error);
  						});
  
  					self.dialog.elfinderdialog('close');
  				};
  
  				opts.buttons[fm.i18n('btnCancel')] = function() {
  					self.dialog.elfinderdialog('close');
  				};
  				
  				dialog = fm.dialog(content, opts);
  				dialog.ready(function(){
  					inputs.protocol.change();
  					dialog.elfinderdialog('posInit');
  				});
  				return dialog;
  			}
  			;
  		
  		if (!self.dialog) {
  			self.dialog = create();
  		} else {
  			self.dialog.elfinderdialog('open');
  		}
  
  		return dfrd.promise();
  	}
  
  	self.fm.bind('netmount', function(e) {
  		var d = e.data || null,
  			o = self.options;
  		if (d && d.protocol) {
  			if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
  				o[d.protocol].done(self.fm, d);
  			}
  		}
  	});
  
  }
  
  elFinder.prototype.commands.netunmount = function() {
  	var self = this;
  
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  
  	this.drivers = [];
  	
  	this.handlers = {
  		load : function() {
  			this.drivers = this.fm.netDrivers;
  		}
  	};
  
  	this.getstate = function(sel) {
  		var fm = this.fm;
  		return !!sel && this.drivers.length && !this._disabled && fm.file(sel[0]).netkey ? 0 : -1;
  	};
  	
  	this.exec = function(hashes) {
  		var self   = this,
  			fm     = this.fm,
  			dfrd   = $.Deferred()
  				.fail(function(error) {
  					error && fm.error(error);
  				}),
  			drive  = fm.file(hashes[0]);
  
  		if (this._disabled) {
  			return dfrd.reject();
  		}
  
  		if (dfrd.state() == 'pending') {
  			fm.confirm({
  				title  : self.title,
  				text   : fm.i18n('confirmUnmount', drive.name),
  				accept : {
  					label    : 'btnUnmount',
  					callback : function() {  
  						var chDrive = (fm.root() == drive.hash),
  							base = $('#'+fm.navHash2Id(drive.hash)).parent(),
  							navTo = (base.next().length? base.next() : base.prev()).find('.elfinder-navbar-root');
  						fm.request({
  							data   : {cmd  : 'netmount', protocol : 'netunmount', host: drive.netkey, user : drive.hash, pass : 'dum'}, 
  							notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  							preventFail : true
  						})
  						.fail(function(error) {
  							dfrd.reject(error);
  						})
  						.done(function(data) {
  							var open = fm.root();
  							if (chDrive) {
  								if (navTo.length) {
  									open = fm.navId2Hash(navTo[0].id);
  								} else {
  									var files = fm.files();
  									for (var i in files) {
  										if (fm.file(i).mime == 'directory') {
  											open = i;
  											break;
  										}
  									}
  								}
  								fm.exec('open', open);
  							}
  							dfrd.resolve();
  						});
  					}
  				},
  				cancel : {
  					label    : 'btnCancel',
  					callback : function() { dfrd.reject(); }
  				}
  			});
  		}
  			
  		return dfrd;
  	};
  
  };
  
  
  /*
   * File: /js/commands/open.js
   */
  
  /**
   * @class  elFinder command "open"
   * Enter folder or open files in new windows
   *
   * @author Dmitry (dio) Levashov
   **/  
  elFinder.prototype.commands.open = function() {
  	this.alwaysEnabled = true;
  	
  	this._handlers = {
  		dblclick : function(e) { e.preventDefault(); this.exec() },
  		'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0));  }
  	}
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+down numpad_enter'+(this.fm.OS != 'mac' && ' enter')
  	}];
  
  	this.getstate = function(sel) {
  		var sel = this.files(sel),
  			cnt = sel.length;
  		
  		return cnt == 1 
  			? 0 
  			: (cnt && !this.fm.UA.Mobile) ? ($.map(sel, function(file) { return file.mime == 'directory' ? null : file}).length == cnt ? 0 : -1) : -1
  	}
  	
  	this.exec = function(hashes, opts) {
  		var fm    = this.fm, 
  			dfrd  = $.Deferred().fail(function(error) { error && fm.error(error); }),
  			files = this.files(hashes),
  			cnt   = files.length,
  			thash = (typeof opts == 'object')? opts.thash : false,
  			file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline;
  
  		if (!cnt && !thash) {
  			{
  				return dfrd.reject();
  			}
  		}
  
  		// open folder
  		if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) {
  			return !thash && file && !file.read
  				? dfrd.reject(['errOpen', file.name, 'errPerm'])
  				: fm.request({
  						data   : {cmd  : 'open', target : thash || file.hash},
  						notify : {type : 'open', cnt : 1, hideCnt : true},
  						syncOnFail : true
  					});
  		}
  		
  		files = $.map(files, function(file) { return file.mime != 'directory' ? file : null });
  		
  		// nothing to open or files and folders selected - do nothing
  		if (cnt != files.length) {
  			return dfrd.reject();
  		}
  		
  		var doOpen = function() {
  			try {
  				reg = new RegExp(fm.option('dispInlineRegex'));
  			} catch(e) {
  				reg = false;
  			}
  	
  			// open files
  			link     = $('<a>').hide().appendTo($('body')),
  			html5dl  = (typeof link.get(0).download === 'string');
  			cnt = files.length;
  			while (cnt--) {
  				file = files[cnt];
  				
  				if (!file.read) {
  					return dfrd.reject(['errOpen', file.name, 'errPerm']);
  				}
  				
  				inline = (reg && file.mime.match(reg));
  				url = fm.openUrl(file.hash, !inline);
  				if (fm.UA.Mobile || !inline) {
  					if (html5dl) {
  						!inline && link.attr('download', file.name);
  						link.attr('href', url)
  						.attr('target', '_blank')
  						.get(0).click();
  					} else {
  						var wnd = window.open(url);
  						if (!wnd) {
  							return dfrd.reject('errPopup');
  						}
  					}
  				} else {
  					
  					// set window size for image if set
  					imgW = winW = Math.round(2 * $(window).width() / 3);
  					imgH = winH = Math.round(2 * $(window).height() / 3);
  					if (parseInt(file.width) && parseInt(file.height)) {
  						imgW = parseInt(file.width);
  						imgH = parseInt(file.height);
  					} else if (file.dim) {
  						s = file.dim.split('x');
  						imgW = parseInt(s[0]);
  						imgH = parseInt(s[1]);
  					}
  					if (winW >= imgW && winH >= imgH) {
  						winW = imgW;
  						winH = imgH;
  					} else {
  						if ((imgW - winW) > (imgH - winH)) {
  							winH = Math.round(imgH * (winW / imgW));
  						} else {
  							winW = Math.round(imgW * (winH / imgH));
  						}
  					}
  					w = 'width='+winW+',height='+winH;
  		
  					if (url.indexOf(fm.options.url) === 0) {
  						url = '';
  					}
  					var wnd = window.open(url, 'new_window', w + ',top=50,left=50,scrollbars=yes,resizable=yes');
  					if (!wnd) {
  						return dfrd.reject('errPopup');
  					}
  					
  					if (url === '') {
  						var form = document.createElement("form");
  						form.action = fm.options.url;
  						form.method = 'POST';
  						form.target = 'new_window';
  						form.style.display = 'none';
  						var params = $.extend({}, fm.options.customData, {
  							cmd: 'file',
  							target: file.hash
  						});
  						$.each(params, function(key, val)
  						{
  							var input = document.createElement("input");
  							input.name = key;
  							input.value = val;
  							form.appendChild(input);
  						});
  						
  						document.body.appendChild(form);
  						form.submit();
  					}
  					wnd.focus();
  					
  				}
  			}
  			link.remove();
  			return dfrd.resolve(hashes);
  		}
  		
  		if (cnt > 1) {
  			fm.confirm({
  				title: 'openMulti',
  				text : ['openMultiConfirm', cnt + ''],
  				accept : {
  					label : 'cmdopen',
  					callback : function() { doOpen(); }
  				},
  				cancel : {
  					label : 'btnCancel',
  					callback : function() { 
  						dfrd.reject();
  					}
  				},
  				buttons : (fm.command('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [
  					{
  						label : 'cmddownload',
  						callback : function() {
  							fm.exec('download', hashes);
  							dfrd.reject();
  						}
  					}
  				] : []
  			});
  		} else {
  			doOpen();
  		}
  		
  		return dfrd;
  	}
  
  };
  
  
  /*
   * File: /js/commands/opendir.js
   */
  
  /**
   * @class  elFinder command "opendir"
   * Enter parent folder
   *
   * @author Naoki Sawada
   **/  
  elFinder.prototype.commands.opendir = function() {
  	this.alwaysEnabled = true;
  	
  	this.getstate = function() {
  		var sel = this.fm.selected(),
  			cnt = sel.length,
  			wz;
  		if (cnt !== 1) {
  			return -1;
  		}
  		wz = this.fm.getUI('workzone');
  		return wz.hasClass('elfinder-search-result')? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var fm    = this.fm,
  			dfrd  = $.Deferred(),
  			files = this.files(hashes),
  			cnt   = files.length,
  			hash, pcheck = null;
  
  		if (!cnt || !files[0].phash) {
  			return dfrd.reject();
  		}
  
  		hash = files[0].phash;
  		if (!fm.file(hash)) {
  			// parents check
  			pcheck = fm.request({
  				data   : {cmd  : 'parents', target : hash},
  				syncOnFail : false
  			});
  		}
  		// open folder
  		$.when(pcheck)
  		.done(function(data){
  			fm.trigger('searchend', { noupdate: true });
  			fm.request({
  				data   : {cmd  : 'open', target : hash},
  				notify : {type : 'open', cnt : 1, hideCnt : true},
  				syncOnFail : false
  			});
  		});
  		
  		return dfrd;
  	}
  
  };
  
  
  /*
   * File: /js/commands/paste.js
   */
  
  /**
   * @class  elFinder command "paste"
   * Paste filesfrom clipboard into directory.
   * If files pasted in its parent directory - files duplicates will created
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.paste = function() {
  	
  	this.updateOnSelect  = false;
  	
  	this.handlers = {
  		changeclipboard : function() { this.update(); }
  	}
  
  	this.shortcuts = [{
  		pattern     : 'ctrl+v shift+insert'
  	}];
  	
  	this.getstate = function(dst) {
  		if (this._disabled) {
  			return -1;
  		}
  		if (dst) {
  			if ($.isArray(dst)) {
  				if (dst.length != 1) {
  					return -1;
  				}
  				dst = this.fm.file(dst[0]);
  			}
  		} else {
  			dst = this.fm.cwd();
  		}
  
  		return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1;
  	}
  	
  	this.exec = function(dst) {
  		var self   = this,
  			fm     = self.fm,
  			dst    = dst ? this.files(dst)[0] : fm.cwd(),
  			files  = fm.clipboard(),
  			cnt    = files.length,
  			cut    = cnt ? files[0].cut : false,
  			error  = cut ? 'errMove' : 'errCopy',
  			fpaste = [],
  			fcopy  = [],
  			dfrd   = $.Deferred()
  				.fail(function(error) {
  					error && fm.error(error);
  				})
  				.always(function() {
  					fm.unlockfiles({files : $.map(files, function(f) { return f.hash})});
  				}),
  			copy  = function(files) {
  				return files.length && fm._commands.duplicate
  					? fm.exec('duplicate', files)
  					: $.Deferred().resolve();
  			},
  			paste = function(files) {
  				var dfrd      = $.Deferred(),
  					existed   = [],
  					hashes  = {},
  					intersect = function(files, names) {
  						var ret = [], 
  							i   = files.length;
  
  						while (i--) {
  							$.inArray(files[i].name, names) !== -1 && ret.unshift(i);
  						}
  						return ret;
  					},
  					confirm   = function(ndx) {
  						var i    = existed[ndx],
  							file = files[i],
  							last = ndx == existed.length-1;
  
  						if (!file) {
  							return;
  						}
  
  						fm.confirm({
  							title  : fm.i18n(cut ? 'moveFiles' : 'copyFiles'),
  							text   : ['errExists', file.name, 'confirmRepl'], 
  							all    : !last,
  							accept : {
  								label    : 'btnYes',
  								callback : function(all) {
  									!last && !all
  										? confirm(++ndx)
  										: paste(files);
  								}
  							},
  							reject : {
  								label    : 'btnNo',
  								callback : function(all) {
  									var i;
  
  									if (all) {
  										i = existed.length;
  										while (ndx < i--) {
  											files[existed[i]].remove = true
  										}
  									} else {
  										files[existed[ndx]].remove = true;
  									}
  
  									!last && !all
  										? confirm(++ndx)
  										: paste(files);
  								}
  							},
  							cancel : {
  								label    : 'btnCancel',
  								callback : function() {
  									dfrd.resolve();
  								}
  							},
  							buttons : [
  								{
  									label : 'btnBackup',
  									callback : function(all) {
  										var i;
  										if (all) {
  											i = existed.length;
  											while (ndx < i--) {
  												files[existed[i]].rename = true
  											}
  										} else {
  											files[existed[ndx]].rename = true;
  										}
  										!last && !all
  											? confirm(++ndx)
  											: paste(files);
  									}
  								}
  							]
  						})
  					},
  					valid     = function(names) {
  						var exists = {}, existedArr;
  						if (names) {
  							if ($.isArray(names)) {
  								if (names.length) {
  									if (typeof names[0] == 'string') {
  										// elFinder <= 2.1.6 command `is` results
  										existed = intersect(files, names);
  									} else {
  										$.each(names, function(i, v) {
  											exists[v.name] = v.hash;
  										});
  										existed = intersect(files, $.map(exists, function(h, n) { return n; }));
  										$.each(files, function(i, file) {
  											if (exists[file.name]) {
  												hashes[exists[file.name]] = file.name;
  											}
  										});
  									}
  								}
  							} else {
  								existedArr = [];
  								existed = $.map(names, function(n) {
  									if (typeof n === 'string') {
  										return n;
  									} else {
  										// support to >=2.1.11 plugin Normalizer, Sanitizer
  										existedArr = existedArr.concat(n);
  										return null;
  									}
  								});
  								if (existedArr.length) {
  									existed = existed.concat(existedArr);
  								}
  								existed = intersect(files, existed);
  								hashes = names;
  							}
  						}
  						existed.length ? confirm(0) : paste(files);
  					},
  					paste     = function(files) {
  						var renames = [],
  							files  = $.map(files, function(file) { 
  								if (file.rename) {
  									renames.push(file.name);
  								}
  								return !file.remove ? file : null;
  							}),
  							cnt    = files.length,
  							groups = {},
  							args   = [],
  							src;
  
  						if (!cnt) {
  							return dfrd.resolve();
  						}
  
  						src = files[0].phash;
  						files = $.map(files, function(f) { return f.hash; });
  						
  						fm.request({
  								data   : {cmd : 'paste', dst : dst.hash, targets : files, cut : cut ? 1 : 0, src : src, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix},
  								notify : {type : cut ? 'move' : 'copy', cnt : cnt}
  							})
  							.done(function(data) {
  								dfrd.resolve(data);
  								if (data && data.added && data.added[0]) {
  									var newItem = fm.getUI('cwd').find('#'+fm.cwdHash2Id(data.added[0].hash));
  									if (newItem.length) {
  										newItem.trigger('scrolltoview');
  									}
  								}
  							})
  							.always(function() {
  								fm.unlockfiles({files : files});
  							});
  					},
  					internames;
  
  				if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) {
  					return dfrd.resolve();
  				}
  				
  					
  				if (fm.oldAPI) {
  					paste(files);
  				} else {
  					
  					if (!fm.option('copyOverwrite')) {
  						paste(files);
  					} else {
  						internames = $.map(files, function(f) { return f.name});
  						dst.hash == fm.cwd().hash
  							? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null }))
  							: fm.request({
  								data : {cmd : 'ls', target : dst.hash, intersect : internames},
  								notify : {type : 'prepare', cnt : 1, hideCnt : true},
  								preventFail : true
  							})
  							.always(function(data) {
  								valid(data.list);
  							});
  					}
  				}
  				
  				return dfrd;
  			},
  			parents, fparents;
  
  
  		if (!cnt || !dst || dst.mime != 'directory') {
  			return dfrd.reject();
  		}
  			
  		if (!dst.write)	{
  			return dfrd.reject([error, files[0].name, 'errPerm']);
  		}
  		
  		parents = fm.parents(dst.hash);
  		
  		$.each(files, function(i, file) {
  			if (!file.read) {
  				return !dfrd.reject([error, files[0].name, 'errPerm']);
  			}
  			
  			if (cut && file.locked) {
  				return !dfrd.reject(['errLocked', file.name]);
  			}
  			
  			if ($.inArray(file.hash, parents) !== -1) {
  				return !dfrd.reject(['errCopyInItself', file.name]);
  			}
  			
  			fparents = fm.parents(file.hash);
  			fparents.pop();
  			if ($.inArray(dst.hash, fparents) !== -1) {
  				
  				if ($.map(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? d : null }).length) {
  					return !dfrd.reject(['errReplByChild', file.name]);
  				}
  			}
  			
  			if (file.phash == dst.hash) {
  				fcopy.push(file.hash);
  			} else {
  				fpaste.push({
  					hash  : file.hash,
  					phash : file.phash,
  					name  : file.name
  				});
  			}
  		});
  
  		if (dfrd.state() == 'rejected') {
  			return dfrd;
  		}
  
  		return $.when(
  			copy(fcopy),
  			paste(fpaste)
  		).always(function() {
  			cut && fm.clipboard([]);
  		});
  	}
  
  };
  
  
  /*
   * File: /js/commands/places.js
   */
  
  /**
   * @class  elFinder command "places"
   * Regist to Places
   *
   * @author Naoki Sawada
   **/
  elFinder.prototype.commands.places = function() {
  	var self   = this,
  	fm     = this.fm,
  	filter = function(hashes) {
  		return $.map(self.files(hashes), function(f) { return f.mime == 'directory' ? f : null; });
  	},
  	places = null;
  	
  	this.getstate = function(sel) {
  		var sel = this.hashes(sel),
  		cnt = sel.length;
  		
  		return  places && cnt && cnt == filter(sel).length ? 0 : -1;
  	};
  	
  	this.exec = function(hashes) {
  		var files = this.files(hashes);
  		places.trigger('regist', [ files ]);
  	};
  	
  	fm.one('load', function(){
  		places = fm.ui.places;
  	});
  
  };
  
  
  /*
   * File: /js/commands/quicklook.js
   */
  
  /**
   * @class  elFinder command "quicklook"
   * Fast preview for some files types
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.quicklook = function() {
  	var self       = this,
  		fm         = self.fm,
  		/**
  		 * window closed state
  		 *
  		 * @type Number
  		 **/
  		closed     = 0,
  		/**
  		 * window animated state
  		 *
  		 * @type Number
  		 **/
  		animated   = 1,
  		/**
  		 * window opened state
  		 *
  		 * @type Number
  		 **/
  		opened     = 2,
  		/**
  		 * window state
  		 *
  		 * @type Number
  		 **/
  		state      = closed,
  		/**
  		 * next/prev event name (requied to cwd catch it)
  		 *
  		 * @type Number
  		 **/
  		// keydown    = fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  		/**
  		 * navbar icon class
  		 *
  		 * @type Number
  		 **/
  		navicon    = 'elfinder-quicklook-navbar-icon',
  		/**
  		 * navbar "fullscreen" icon class
  		 *
  		 * @type Number
  		 **/
  		fullscreen  = 'elfinder-quicklook-fullscreen',
  		/**
  		 * Triger keydown/keypress event with left/right arrow key code
  		 *
  		 * @param  Number  left/right arrow key code
  		 * @return void
  		 **/
  		navtrigger = function(code) {
  			$(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  		},
  		/**
  		 * Return css for closed window
  		 *
  		 * @param  jQuery  file node in cwd
  		 * @return void
  		 **/
  		closedCss = function(node) {
  			return {
  				opacity : 0,
  				width   : 20,//node.width(),
  				height  : fm.view == 'list' ? 1 : 20,
  				top     : node.offset().top+'px', 
  				left    : node.offset().left+'px' 
  			}
  		},
  		/**
  		 * Return css for opened window
  		 *
  		 * @return void
  		 **/
  		openedCss = function() {
  			var win = $(window);
  			var w = Math.min(width, $(window).width()-10);
  			var h = Math.min(height, $(window).height()-80);
  			return {
  				opacity : 1,
  				width  : w,
  				height : h,
  				top    : parseInt((win.height() - h - 60)/2 + win.scrollTop()),
  				left   : parseInt((win.width() - w)/2 + win.scrollLeft())
  			}
  		},
  		
  		support = function(codec) {
  			var media = document.createElement(codec.substr(0, codec.indexOf('/'))),
  				value = false;
  			
  			try {
  				value = media.canPlayType && media.canPlayType(codec);
  			} catch (e) {
  				
  			}
  			
  			return value && value !== '' && value != 'no';
  		},
  		
  		/**
  		 * Opened window width (from config)
  		 *
  		 * @type Number
  		 **/
  		width, 
  		/**
  		 * Opened window height (from config)
  		 *
  		 * @type Number
  		 **/
  		height, 
  		/**
  		 * elFinder node
  		 *
  		 * @type jQuery
  		 **/
  		parent, 
  		/**
  		 * elFinder current directory node
  		 *
  		 * @type jQuery
  		 **/
  		cwd, 
  		navdrag = false,
  		navmove = false,
  		navtm   = null,
  		coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'),
  		title   = $('<div class="elfinder-quicklook-title"/>'),
  		icon    = $('<div/>'),
  		info    = $('<div class="elfinder-quicklook-info"/>'),//.hide(),
  		cover   = $('<div class="ui-front elfinder-quicklook-cover"/>'),
  		fsicon  = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>')
  			.on('click touchstart', function(e) {
  				if (navmove) {
  					return;
  				}
  				
  				var win     = self.window,
  					full    = win.hasClass(fullscreen),
  					scroll  = 'scroll.'+fm.namespace,
  					$window = $(window);
  					
  				e.stopPropagation();
  				e.preventDefault();
  				
  				win.toggleClass(fullscreen);
  				if (full) {
  					win.css(win.data('position'));
  					$window.off(scroll).trigger(self.resize).off(self.resize);
  					navbar.off('mouseenter mouseleave');
  					cover.off(coverEv);
  					navStyle = '';
  					navShow();
  				} else {
  					win.data('position', {
  						left   : win.css('left'), 
  						top    : win.css('top'), 
  						width  : win.width(), 
  						height : win.height(),
  						display: 'block'
  					})
  					.removeAttr('style');
  
  					$(window).on(self.resize, function(e) {
  						self.preview.trigger('changesize');
  					})
  					.trigger(scroll)
  					.trigger(self.resize);
  
  					cover.on(coverEv, function(e) {
  						if (! navdrag) {
  							if (e.type === 'mousemove' || e.type === 'touchstart') {
  								navShow();
  								navtm = setTimeout(function() {
  									if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) {
  										navbar.fadeOut('slow', function() {
  											cover.show();
  										});
  									}
  								}, 3000);
  							}
  							if (cover.is(':visible')) {
  								coverHide();
  								cover.data('tm', setTimeout(function() {
  									cover.show();
  								}, 3000));
  							}
  						}
  					}).show().trigger('mousemove');
  					
  					navbar.on('mouseenter mouseleave', function(e) {
  						if (! navdrag) {
  							if (e.type === 'mouseenter') {
  								navShow();
  							} else {
  								cover.trigger('mousemove');
  							}
  						}
  					});
  				}
  				if (fm.zIndex) {
  					win.css('z-index', fm.zIndex + 1);
  				}
  				if (fm.UA.Mobile) {
  					navbar.attr('style', navStyle);
  				} else {
  					navbar.attr('style', navStyle).draggable(full ? 'destroy' : {
  						start: function() {
  							navdrag = true;
  							navmove = true;
  							cover.show();
  							navShow();
  						},
  						stop: function() {
  							navdrag = false;
  							navStyle = self.navbar.attr('style');
  							setTimeout(function() {
  								navmove = false;
  							}, 20);
  						}
  					});
  				}
  				$(this).toggleClass(navicon+'-fullscreen-off');
  				var collection = win;
  				if (parent.is('.ui-resizable')) {
  					collection = collection.add(parent);
  				};
  				$.fn.resizable && collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled');
  
  				win.trigger('viewchange');
  			}),
  		
  		navShow = function() {
  			if (self.window.hasClass(fullscreen)) {
  				navtm && clearTimeout(navtm);
  				// if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0)
  				// see #1478 https://github.com/Studio-42/elFinder/issues/1478
  				navbar.stop(true, true).css('display', 'block');
  				coverHide();
  			}
  		},
  		
  		coverHide = function() {
  			cover.data('tm') && clearTimeout(cover.data('tm'));
  			cover.hide();
  		},
  			
  		prev = $('<div class="'+navicon+' '+navicon+'-prev"/>').on('click touchstart', function(e) { ! navmove && navtrigger(37); return false; }),
  		next = $('<div class="'+navicon+' '+navicon+'-next"/>').on('click touchstart', function(e) { ! navmove && navtrigger(39); return false; }),
  		navbar  = $('<div class="elfinder-quicklook-navbar"/>')
  			.append(prev)
  			.append(fsicon)
  			.append(next)
  			.append('<div class="elfinder-quicklook-navbar-separator"/>')
  			.append($('<div class="'+navicon+' '+navicon+'-close"/>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; }))
  		,
  		navStyle = '';
  
  	(this.navbar = navbar)._show = navShow;
  	this.resize = 'resize.'+fm.namespace;
  	this.info = $('<div class="elfinder-quicklook-info-wrapper"/>')
  		.append(icon)
  		.append(info);
  		
  	this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>')
  		// clean info/icon
  		.on('change', function(e) {
  			navShow();
  			navbar.attr('style', navStyle);
  			self.preview.attr('style', '')
  			self.info.attr('style', '').hide();
  			icon.removeAttr('class').attr('style', '');
  			info.html('');
  		})
  		// update info/icon
  		.on('update', function(e) {
  			var fm      = self.fm,
  				preview = self.preview,
  				file    = e.file,
  				tpl     = '<div class="elfinder-quicklook-info-data">{value}</div>',
  				tmb;
  
  			if (file && self.window.data('hash') !== file.hash) {
  				!file.read && e.stopImmediatePropagation();
  				self.window.data('hash', file.hash);
  				self.preview.off('changesize').trigger('change').children().remove();
  				title.html(fm.escape(file.name));
  				
  				prev.css('visibility', '');
  				next.css('visibility', '');
  				if (file.hash === fm.cwdId2Hash(cwd.find('[id]:first').attr('id'))) {
  					prev.css('visibility', 'hidden');
  				}
  				if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) {
  					next.css('visibility', 'hidden');
  				}
  				
  				info.html(
  						tpl.replace(/\{value\}/, fm.escape(file.name))
  						+ tpl.replace(/\{value\}/, fm.mime2kind(file))
  						+ (file.mime == 'directory' ? '' : tpl.replace(/\{value\}/, fm.formatSize(file.size)))
  						+ tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file))
  					)
  				icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime));
  
  				if (tmb = fm.tmb(file)) {
  					$('<img/>')
  						.hide()
  						.appendTo(self.preview)
  						.on('load', function() {
  							icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  							$(this).remove();
  						})
  						.attr('src', tmb.url);
  				}
  				self.info.delay(100).fadeIn(10);
  				if (self.window.hasClass(fullscreen)) {
  					cover.trigger('mousemove');
  				}
  			} else { 
  				e.stopImmediatePropagation();
  			}
  		});
  		
  
  	
  
  	this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"/>')
  		.addClass(fm.UA.Touch? 'elfinder-touch' : '')
  		.click(function(e) { e.stopPropagation();  })
  		.append(
  			$('<div class="elfinder-quicklook-titlebar"/>')
  			.append(
  				title,
  				$('<span class="ui-icon ui-icon-circle-close"/>').mousedown(function(e) {
  					e.stopPropagation();
  					self.window.trigger('close');
  				})),
  				this.preview,
  				self.info.hide(),
  				cover.hide(),
  				navbar
  			)
  		.draggable({handle : 'div.elfinder-quicklook-titlebar'})
  		.on('open', function(e) {
  			var win  = self.window, 
  				file = self.value,
  				node;
  
  			if (self.closed() && file && (node = $('#'+fm.cwdHash2Id(file.hash))).length) {
  				navStyle = '';
  				navbar.attr('style', '');
  				state = animated;
  				node.trigger('scrolltoview');
  				coverHide();
  				win.css(closedCss(node))
  					.show()
  					.animate(openedCss(), 550, function() {
  						state = opened;
  						self.update(1, self.value);
  						navShow();
  					});
  			}
  		})
  		.on('close', function(e) {
  			var win     = self.window,
  				preview = self.preview.trigger('change'),
  				file    = self.value,
  				node    = cwd.find('#'+fm.cwdHash2Id(win.data('hash'))),
  				close   = function() {
  					state = closed;
  					win.hide();
  					preview.children().remove();
  					self.update(0, self.value);
  					
  				};
  				
  			win.data('hash', '');
  			if (self.opened()) {
  				state = animated;
  				win.hasClass(fullscreen) && fsicon.click();
  				node.length
  					? win.animate(closedCss(node), 500, close)
  					: close();
  			}
  		});
  
  	/**
  	 * This command cannot be disable by backend
  	 *
  	 * @type Boolean
  	 **/
  	this.alwaysEnabled = true;
  	
  	/**
  	 * Selected file
  	 *
  	 * @type Object
  	 **/
  	this.value = null;
  	
  	this.handlers = {
  		// save selected file
  		select : function() { this.update(void(0), this.fm.selectedFiles()[0]); },
  		error  : function() { self.window.is(':visible') && self.window.data('hash', '').trigger('close'); },
  		'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); }
  	};
  	
  	this.shortcuts = [{
  		pattern     : 'space'
  	}];
  	
  	this.support = {
  		audio : {
  			ogg : support('audio/ogg; codecs="vorbis"'),
  			mp3 : support('audio/mpeg;'),
  			wav : support('audio/wav; codecs="1"'),
  			m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;')
  		},
  		video : {
  			ogg  : support('video/ogg; codecs="theora"'),
  			webm : support('video/webm; codecs="vp8, vorbis"'),
  			mp4  : support('video/mp4; codecs="avc1.42E01E"') || support('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') 
  		}
  	};
  	
  	/**
  	 * Return true if quickLoock window is visible and not animated
  	 *
  	 * @return Boolean
  	 **/
  	this.closed = function() {
  		return state == closed;
  	};
  	
  	/**
  	 * Return true if quickLoock window is hidden
  	 *
  	 * @return Boolean
  	 **/
  	this.opened = function() {
  		return state == opened;
  	};
  	
  	/**
  	 * Init command.
  	 * Add default plugins and init other plugins
  	 *
  	 * @return Object
  	 **/
  	this.init = function() {
  		var o       = this.options, 
  			win     = this.window,
  			preview = this.preview,
  			i, p;
  		
  		width  = o.width  > 0 ? parseInt(o.width)  : 450;	
  		height = o.height > 0 ? parseInt(o.height) : 300;
  
  		fm.one('load', function() {
  			parent = fm.getUI();
  			cwd    = fm.getUI('cwd');
  
  			if (fm.zIndex) {
  				win.css('z-index', fm.zIndex + 1);
  			}
  			
  			win.appendTo('body');
  			
  			// close window on escape
  			$(document).keydown(function(e) {
  				e.keyCode == 27 && self.opened() && win.trigger('close')
  			})
  			
  			if ($.fn.resizable) {
  				win.resizable({ 
  					handles   : 'se', 
  					minWidth  : 350, 
  					minHeight : 120, 
  					resize    : function() { 
  						// use another event to avoid recursion in fullscreen mode
  						// may be there is clever solution, but i cant find it :(
  						preview.trigger('changesize'); 
  					}
  				});
  			}
  			
  			self.change(function() {
  				if (self.opened()) {
  					self.value ? preview.trigger($.Event('update', {file : self.value})) : win.trigger('close');
  				}
  			});
  			
  			$.each(fm.commands.quicklook.plugins || [], function(i, plugin) {
  				if (typeof(plugin) == 'function') {
  					new plugin(self)
  				}
  			});
  			
  			preview.on('update', function() {
  				self.info.show();
  			});
  		});
  		
  		fm.bind('open',function() {
  			// set current volume dispInlineRegex
  			try {
  				self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex'));
  			} catch(e) {
  				self.dispInlineRegex = /.*/;
  			}
  		});
  		
  		fm.bind('destroy', function() {
  			self.window.remove();
  		});
  	};
  	
  	this.getstate = function() {
  		var fm  = this.fm,
  			sel = fm.selected(),
  			chk = sel.length === 1 && $('#'+fm.cwdHash2Id(sel[0])).length;
  		return chk? state == opened ? 1 : 0 : -1;
  	};
  	
  	this.exec = function() {
  		this.enabled() && this.window.trigger(this.opened() ? 'close' : 'open');
  	};
  
  	this.hideinfo = function() {
  		this.info.stop(true, true).hide();
  	};
  
  };
  
  
  /*
   * File: /js/commands/quicklook.plugins.js
   */
  
  
  elFinder.prototype.commands.quicklook.plugins = [
  	
  	/**
  	 * Images preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var mimes   = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
  			preview = ql.preview;
  		
  		// what kind of images we can display
  		$.each(navigator.mimeTypes, function(i, o) {
  			var mime = o.type;
  			
  			if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) {
  				mimes.push(mime);
  			} 
  		});
  			
  		preview.on('update', function(e) {
  			var fm   = ql.fm,
  				file = e.file,
  				img, loading;
  
  			if (ql.dispInlineRegex.test(file.mime) && $.inArray(file.mime, mimes) !== -1) {
  				// this is our file - stop event propagation
  				e.stopImmediatePropagation();
  
  				loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  
  				img = $('<img/>')
  					.hide()
  					.appendTo(preview)
  					.on('load', function() {
  						// timeout - because of strange safari bug - 
  						// sometimes cant get image height 0_o
  						setTimeout(function() {
  							var prop = (img.width()/img.height()).toFixed(2);
  							preview.on('changesize', function() {
  								var pw = parseInt(preview.width()),
  									ph = parseInt(preview.height()),
  									w, h;
  							
  								if (prop < (pw/ph).toFixed(2)) {
  									h = ph;
  									w = Math.floor(h * prop);
  								} else {
  									w = pw;
  									h = Math.floor(w/prop);
  								}
  								img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  							
  							})
  							.trigger('changesize');
  							
  							loading.remove();
  							// hide info/icon
  							ql.hideinfo();
  							//show image
  							img.fadeIn(100);
  						}, 1)
  					})
  					.on('error', function() {
  						loading.remove();
  					})
  					.attr('src', ql.fm.openUrl(file.hash));
  			}
  			
  		});
  	},
  	
  	/**
  	 * HTML preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var mimes   = ['text/html', 'application/xhtml+xml'],
  			preview = ql.preview,
  			fm      = ql.fm;
  			
  		preview.on('update', function(e) {
  			var file = e.file, jqxhr, loading;
  			
  			if (ql.dispInlineRegex.test(file.mime) && $.inArray(file.mime, mimes) !== -1) {
  				e.stopImmediatePropagation();
  
  				loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  
  				// stop loading on change file if not loaded yet
  				preview.one('change', function() {
  					jqxhr.state() == 'pending' && jqxhr.reject();
  				});
  				
  				jqxhr = fm.request({
  					data           : {cmd : 'get', target  : file.hash, current : file.phash, conv : 1},
  					preventDefault : true
  				})
  				.done(function(data) {
  					ql.hideinfo();
  					var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  					doc.open();
  					doc.write(data.content);
  					doc.close();
  				})
  				.always(function() {
  					loading.remove();
  				});
  			}
  		})
  	},
  	
  	/**
  	 * Texts preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var fm      = ql.fm,
  			mimes   = fm.res('mimes', 'text'),
  			preview = ql.preview;
  				
  			
  		preview.on('update', function(e) {
  			var file = e.file,
  				mime = file.mime,
  				jqxhr, loading;
  			
  			if (mime.indexOf('text/') === 0 || $.inArray(mime, mimes) !== -1) {
  				e.stopImmediatePropagation();
  				
  				loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  
  				// stop loading on change file if not loadin yet
  				preview.one('change', function() {
  					jqxhr.state() == 'pending' && jqxhr.reject();
  				});
  				
  				jqxhr = fm.request({
  					data   : {cmd     : 'get', target  : file.hash, conv : 1},
  					preventDefault : true
  				})
  				.done(function(data) {
  					ql.hideinfo();
  					$('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text">'+fm.escape(data.content)+'</pre></div>').appendTo(preview);
  				})
  				.always(function() {
  					loading.remove();
  				});
  			}
  		});
  	},
  	
  	/**
  	 * PDF preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var fm      = ql.fm,
  			mime    = 'application/pdf',
  			preview = ql.preview,
  			active  = false;
  			
  		if ((fm.UA.Safari && fm.OS == 'mac') || fm.UA.IE) {
  			active = true;
  		} else {
  			$.each(navigator.plugins, function(i, plugins) {
  				$.each(plugins, function(i, plugin) {
  					if (plugin.type == mime) {
  						return !(active = true);
  					}
  				});
  			});
  		}
  
  		active && preview.on('update', function(e) {
  			var file = e.file, node;
  			
  			if (ql.dispInlineRegex.test(file.mime) && file.mime == mime) {
  				e.stopImmediatePropagation();
  				preview.one('change', function() {
  					node.off('load').remove();
  				});
  				
  				node = $('<iframe class="elfinder-quicklook-preview-pdf"/>')
  					.hide()
  					.appendTo(preview)
  					.on('load', function() { 
  						ql.hideinfo();
  						node.show(); 
  					})
  					.attr('src', fm.url(file.hash));
  			}
  			
  		})
  		
  			
  	},
  	
  	/**
  	 * Flash preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var fm      = ql.fm,
  			mime    = 'application/x-shockwave-flash',
  			preview = ql.preview,
  			active  = false;
  
  		$.each(navigator.plugins, function(i, plugins) {
  			$.each(plugins, function(i, plugin) {
  				if (plugin.type == mime) {
  					return !(active = true);
  				}
  			});
  		});
  		
  		active && preview.on('update', function(e) {
  			var file = e.file,
  				node;
  				
  			if (ql.dispInlineRegex.test(file.mime) && file.mime == mime) {
  				e.stopImmediatePropagation();
  				ql.hideinfo();
  				node = $('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+fm.url(file.hash)+'" quality="high" type="application/x-shockwave-flash" wmode="transparent" />')
  					.appendTo(preview);
  			}
  		});
  	},
  	
  	/**
  	 * HTML5 audio preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var preview  = ql.preview,
  			autoplay = !!ql.options['autoplay'],
  			mimes    = {
  				'audio/mpeg'    : 'mp3',
  				'audio/mpeg3'   : 'mp3',
  				'audio/mp3'     : 'mp3',
  				'audio/x-mpeg3' : 'mp3',
  				'audio/x-mp3'   : 'mp3',
  				'audio/x-wav'   : 'wav',
  				'audio/wav'     : 'wav',
  				'audio/x-m4a'   : 'm4a',
  				'audio/aac'     : 'm4a',
  				'audio/mp4'     : 'm4a',
  				'audio/x-mp4'   : 'm4a',
  				'audio/ogg'     : 'ogg'
  			},
  			node,
  			win  = ql.window,
  			navi = ql.navbar;
  
  		preview.on('update', function(e) {
  			var file = e.file,
  				type = mimes[file.mime],
  				setNavi = function() {
  					navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  				};
  
  			if (ql.support.audio[type]) {
  				e.stopImmediatePropagation();
  				
  				node = $('<audio class="elfinder-quicklook-preview-audio" controls preload="auto" autobuffer><source src="'+ql.fm.openUrl(file.hash)+'" /></audio>')
  					.appendTo(preview);
  				autoplay && node[0].play();
  				
  				win.on('viewchange.audio', setNavi);
  				setNavi();
  			}
  		}).on('change', function() {
  			if (node && node.parent().length) {
  				win.off('viewchange.audio');
  				node[0].pause();
  				node.remove();
  				node= null;
  			}
  		});
  	},
  	
  	/**
  	 * HTML5 video preview plugin
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var preview  = ql.preview,
  			autoplay = !!ql.options['autoplay'],
  			mimes    = {
  				'video/mp4'       : 'mp4',
  				'video/x-m4v'     : 'mp4',
  				'video/quicktime' : 'mp4',
  				'video/ogg'       : 'ogg',
  				'application/ogg' : 'ogg',
  				'video/webm'      : 'webm'
  			},
  			node,
  			win  = ql.window,
  			navi = ql.navbar;
  
  		preview.on('update', function(e) {
  			var file = e.file,
  				type = mimes[file.mime],
  				setNavi = function() {
  					if (ql.fm.UA.iOS) {
  						if (win.hasClass('elfinder-quicklook-fullscreen')) {
  							preview.css('height', '-webkit-calc(100% - 50px)');
  							navi._show();
  						} else {
  							preview.css('height', '');
  						}
  					} else {
  						navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  					}
  				};
  				
  			if (ql.support.video[type]) {
  				e.stopImmediatePropagation();
  
  				ql.hideinfo();
  				node = $('<video class="elfinder-quicklook-preview-video" controls preload="auto" autobuffer><source src="'+ql.fm.openUrl(file.hash)+'" /></video>').appendTo(preview);
  				autoplay && node[0].play();
  
  				win.on('viewchange.video', setNavi);
  				setNavi();
  			}
  		}).on('change', function() {
  			if (node && node.parent().length) {
  				win.off('viewchange.video');
  				node[0].pause();
  				node.remove();
  				node= null;
  			}
  		});
  	},
  	
  	/**
  	 * Audio/video preview plugin using browser plugins
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var preview = ql.preview,
  			mimes   = [],
  			node,
  			win  = ql.window,
  			navi = ql.navbar;
  			
  		$.each(navigator.plugins, function(i, plugins) {
  			$.each(plugins, function(i, plugin) {
  				(plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type);
  			});
  		});
  		
  		preview.on('update', function(e) {
  			var file  = e.file,
  				mime  = file.mime,
  				video,
  				setNavi = function() {
  					navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  				};
  			
  			if ($.inArray(file.mime, mimes) !== -1) {
  				e.stopImmediatePropagation();
  				(video = mime.indexOf('video/') === 0) && ql.hideinfo();
  				node = $('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>')
  					.appendTo(preview);
  				
  				win.on('viewchange.embed', setNavi);
  				setNavi();
  			}
  		}).on('change', function() {
  			if (node && node.parent().length) {
  				win.off('viewchange.embed');
  				node.remove();
  				node= null;
  			}
  		});
  		
  	},
  
  	/**
  	 * Archive(zip|gzip|tar) preview plugin using https://github.com/imaya/zlib.js
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var mimes   = ['application/zip', 'application/x-gzip', 'application/x-tar'],
  			preview = ql.preview,
  			fm      = ql.fm;
  
  		if (typeof Uint8Array !== 'undefined' && elFinder.Zlib) {
  			preview.on('update', function(e) {
  				var file = e.file,
  					doc, xhr, loading;
  
  				if ($.inArray(file.mime, mimes) !== -1) {
  					// this is our file - stop event propagation
  					e.stopImmediatePropagation();
  					
  					loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  					
  					// stop loading on change file if not loaded yet
  					preview.one('change', function() {
  						loading.remove();
  						xhr && xhr.readyState < 4 && xhr.abort();
  					});
  					
  					xhr = new XMLHttpRequest();
  					xhr.onload = function(e) {
  						var filenames = [], header, unzip, tar, tarlen, offset, h, name, prefix, size, dbs, toStr;
  						if (this.readyState === 4 && this.response) {
  							setTimeout(function() {
  								if (file.mime === 'application/zip') {
  									unzip = new elFinder.Zlib.Unzip(new Uint8Array(xhr.response));
  									filenames = unzip.getFilenames();
  								} else {
  									if (file.mime === 'application/x-gzip') {
  										unzip = new elFinder.Zlib.Gunzip(new Uint8Array(xhr.response));
  										tar = unzip.decompress();
  									} else {
  										tar = new Uint8Array(xhr.response);
  									}
  									tarlen = tar.length;
  									offset = 0;
  									toStr = function(arr) {
  										return String.fromCharCode.apply(null, arr).replace(/\0+$/, '');
  									};
  									while (offset < tarlen && tar[offset] !== 0) {
  										h = tar.subarray(offset, offset + 512);
  										name = toStr(h.subarray(0, 100));
  										if (prefix = toStr(h.subarray(345, 500))) {
  											name = prefix + name;
  										}
  										size = parseInt(toStr(h.subarray(124, 136)), 8);
  										dbs = Math.ceil(size / 512) * 512;
  										if (name === '././@LongLink') {
  											name = toStr(tar.subarray(offset + 512, offset + 512 + dbs));
  										}
  										(name !== 'pax_global_header') && filenames.push(name);
  										offset = offset + 512 + dbs;
  									}
  								}
  								if (filenames && filenames.length) {
  									filenames = $.map(filenames, function(str) {
  										try {
  											str = decodeURIComponent(escape(str));
  										} catch(e) {
  											str = escape(str);
  										}
  										return str;
  									});
  									filenames.sort();
  									loading.remove();
  									header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>'
  									doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>').appendTo(preview);
  									ql.hideinfo();
  								}
  							}, 70);
  						} else {
  							loading.remove();
  						}
  					}
  					xhr.open('GET', fm.openUrl(file.hash, fm.xhrFields.withCredentials || false), true);
  					xhr.responseType = 'arraybuffer';
  					// set request headers
  					if (fm.customHeaders) {
  						$.each(fm.customHeaders, function(key) {
  							xhr.setRequestHeader(key, this);
  						});
  					}
  					// set xhrFields
  					if (fm.xhrFields) {
  						$.each(fm.xhrFields, function(key) {
  							if (key in xhr) {
  								xhr[key] = this;
  							}
  						});
  					}
  					xhr.send();
  				}
  			});
  		}
  	},
  
  	/**
  	 * Any supported files preview plugin using Google docs online viewer
  	 *
  	 * @param elFinder.commands.quicklook
  	 **/
  	function(ql) {
  		var fm      = ql.fm,
  			mimes   = ql.options.googleDocsMimes || [],
  			preview = ql.preview;
  			
  		preview.on('update', function(e) {
  			var win  = ql.window,
  				file = e.file, node, loading;
  			
  			if ($.inArray(file.mime, mimes) !== -1) {
  				if (file.url == '1') {
  					$('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  					.on('click', function() {
  						$(this).html('<span class="elfinder-info-spinner">');
  						fm.request({
  							data : {cmd : 'url', target : file.hash},
  							preventDefault : true
  						})
  						.always(function() {
  							$(this).html('');
  						})
  						.done(function(data) {
  							var rfile = fm.file(file.hash);
  							ql.value.url = rfile.url = data.url || '';
  							if (ql.value.url) {
  								preview.trigger($.Event('update', {file : ql.value}));
  							}
  						});
  					});
  				}
  				if (file.url !== '' && file.url != '1') {
  					e.stopImmediatePropagation();
  					preview.one('change', function() {
  						loading.remove();
  						node.off('load').remove();
  					});
  					
  					loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  					
  					node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  						.css('background-color', 'transparent')
  						.appendTo(preview)
  						.on('load', function() {
  							ql.hideinfo();
  							loading.remove();
  							$(this).css('background-color', '#fff').show();
  						})
  						.attr('src', 'http://docs.google.com/gview?embedded=true&url=' + encodeURIComponent(fm.convAbsUrl(fm.url(file.hash))));
  				}
  			}
  			
  		});
  	}
  
  ];
  
  try {
  (function(){
  
  /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function m(a){throw a;}var q=void 0,u,aa=this;function v(a,b){var c=a.split("."),d=aa;!(c[0]in d)&&d.execScript&&d.execScript("var "+c[0]);for(var f;c.length&&(f=c.shift());)!c.length&&b!==q?d[f]=b:d=d[f]?d[f]:d[f]={}};var w="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;new (w?Uint8Array:Array)(256);var x;for(x=0;256>x;++x)for(var y=x,ba=7,y=y>>>1;y;y>>>=1)--ba;var z=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,
  2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,
  2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,
  2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,
  3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,
  936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],B=w?new Uint32Array(z):z;function C(a){var b=a.length,c=0,d=Number.POSITIVE_INFINITY,f,h,k,e,g,l,p,s,r,A;for(s=0;s<b;++s)a[s]>c&&(c=a[s]),a[s]<d&&(d=a[s]);f=1<<c;h=new (w?Uint32Array:Array)(f);k=1;e=0;for(g=2;k<=c;){for(s=0;s<b;++s)if(a[s]===k){l=0;p=e;for(r=0;r<k;++r)l=l<<1|p&1,p>>=1;A=k<<16|s;for(r=l;r<f;r+=g)h[r]=A;++e}++k;e<<=1;g<<=1}return[h,c,d]};var D=[],E;for(E=0;288>E;E++)switch(!0){case 143>=E:D.push([E+48,8]);break;case 255>=E:D.push([E-144+400,9]);break;case 279>=E:D.push([E-256+0,7]);break;case 287>=E:D.push([E-280+192,8]);break;default:m("invalid literal: "+E)}
  var ca=function(){function a(a){switch(!0){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272,
  a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:m("invalid length: "+a)}}var b=[],c,d;for(c=3;258>=c;c++)d=a(c),b[c]=d[2]<<24|d[1]<<
  16|d[0];return b}();w&&new Uint32Array(ca);function F(a,b){this.l=[];this.m=32768;this.d=this.f=this.c=this.t=0;this.input=w?new Uint8Array(a):a;this.u=!1;this.n=G;this.L=!1;if(b||!(b={}))b.index&&(this.c=b.index),b.bufferSize&&(this.m=b.bufferSize),b.bufferType&&(this.n=b.bufferType),b.resize&&(this.L=b.resize);switch(this.n){case H:this.a=32768;this.b=new (w?Uint8Array:Array)(32768+this.m+258);break;case G:this.a=0;this.b=new (w?Uint8Array:Array)(this.m);this.e=this.X;this.B=this.S;this.q=this.W;break;default:m(Error("invalid inflate mode"))}}
  var H=0,G=1;
  F.prototype.r=function(){for(;!this.u;){var a=I(this,3);a&1&&(this.u=!0);a>>>=1;switch(a){case 0:var b=this.input,c=this.c,d=this.b,f=this.a,h=b.length,k=q,e=q,g=d.length,l=q;this.d=this.f=0;c+1>=h&&m(Error("invalid uncompressed block header: LEN"));k=b[c++]|b[c++]<<8;c+1>=h&&m(Error("invalid uncompressed block header: NLEN"));e=b[c++]|b[c++]<<8;k===~e&&m(Error("invalid uncompressed block header: length verify"));c+k>b.length&&m(Error("input buffer is broken"));switch(this.n){case H:for(;f+k>d.length;){l=
  g-f;k-=l;if(w)d.set(b.subarray(c,c+l),f),f+=l,c+=l;else for(;l--;)d[f++]=b[c++];this.a=f;d=this.e();f=this.a}break;case G:for(;f+k>d.length;)d=this.e({H:2});break;default:m(Error("invalid inflate mode"))}if(w)d.set(b.subarray(c,c+k),f),f+=k,c+=k;else for(;k--;)d[f++]=b[c++];this.c=c;this.a=f;this.b=d;break;case 1:this.q(da,ea);break;case 2:fa(this);break;default:m(Error("unknown BTYPE: "+a))}}return this.B()};
  var J=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],K=w?new Uint16Array(J):J,L=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],M=w?new Uint16Array(L):L,ga=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],O=w?new Uint8Array(ga):ga,ha=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ia=w?new Uint16Array(ha):ha,ja=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,
  12,12,13,13],P=w?new Uint8Array(ja):ja,Q=new (w?Uint8Array:Array)(288),R,la;R=0;for(la=Q.length;R<la;++R)Q[R]=143>=R?8:255>=R?9:279>=R?7:8;var da=C(Q),S=new (w?Uint8Array:Array)(30),T,ma;T=0;for(ma=S.length;T<ma;++T)S[T]=5;var ea=C(S);function I(a,b){for(var c=a.f,d=a.d,f=a.input,h=a.c,k=f.length,e;d<b;)h>=k&&m(Error("input buffer is broken")),c|=f[h++]<<d,d+=8;e=c&(1<<b)-1;a.f=c>>>b;a.d=d-b;a.c=h;return e}
  function U(a,b){for(var c=a.f,d=a.d,f=a.input,h=a.c,k=f.length,e=b[0],g=b[1],l,p;d<g&&!(h>=k);)c|=f[h++]<<d,d+=8;l=e[c&(1<<g)-1];p=l>>>16;a.f=c>>p;a.d=d-p;a.c=h;return l&65535}
  function fa(a){function b(a,b,c){var d,e=this.K,f,g;for(g=0;g<a;)switch(d=U(this,b),d){case 16:for(f=3+I(this,2);f--;)c[g++]=e;break;case 17:for(f=3+I(this,3);f--;)c[g++]=0;e=0;break;case 18:for(f=11+I(this,7);f--;)c[g++]=0;e=0;break;default:e=c[g++]=d}this.K=e;return c}var c=I(a,5)+257,d=I(a,5)+1,f=I(a,4)+4,h=new (w?Uint8Array:Array)(K.length),k,e,g,l;for(l=0;l<f;++l)h[K[l]]=I(a,3);if(!w){l=f;for(f=h.length;l<f;++l)h[K[l]]=0}k=C(h);e=new (w?Uint8Array:Array)(c);g=new (w?Uint8Array:Array)(d);a.K=
  0;a.q(C(b.call(a,c,k,e)),C(b.call(a,d,k,g)))}u=F.prototype;u.q=function(a,b){var c=this.b,d=this.a;this.C=a;for(var f=c.length-258,h,k,e,g;256!==(h=U(this,a));)if(256>h)d>=f&&(this.a=d,c=this.e(),d=this.a),c[d++]=h;else{k=h-257;g=M[k];0<O[k]&&(g+=I(this,O[k]));h=U(this,b);e=ia[h];0<P[h]&&(e+=I(this,P[h]));d>=f&&(this.a=d,c=this.e(),d=this.a);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d};
  u.W=function(a,b){var c=this.b,d=this.a;this.C=a;for(var f=c.length,h,k,e,g;256!==(h=U(this,a));)if(256>h)d>=f&&(c=this.e(),f=c.length),c[d++]=h;else{k=h-257;g=M[k];0<O[k]&&(g+=I(this,O[k]));h=U(this,b);e=ia[h];0<P[h]&&(e+=I(this,P[h]));d+g>f&&(c=this.e(),f=c.length);for(;g--;)c[d]=c[d++-e]}for(;8<=this.d;)this.d-=8,this.c--;this.a=d};
  u.e=function(){var a=new (w?Uint8Array:Array)(this.a-32768),b=this.a-32768,c,d,f=this.b;if(w)a.set(f.subarray(32768,a.length));else{c=0;for(d=a.length;c<d;++c)a[c]=f[c+32768]}this.l.push(a);this.t+=a.length;if(w)f.set(f.subarray(b,b+32768));else for(c=0;32768>c;++c)f[c]=f[b+c];this.a=32768;return f};
  u.X=function(a){var b,c=this.input.length/this.c+1|0,d,f,h,k=this.input,e=this.b;a&&("number"===typeof a.H&&(c=a.H),"number"===typeof a.Q&&(c+=a.Q));2>c?(d=(k.length-this.c)/this.C[2],h=258*(d/2)|0,f=h<e.length?e.length+h:e.length<<1):f=e.length*c;w?(b=new Uint8Array(f),b.set(e)):b=e;return this.b=b};
  u.B=function(){var a=0,b=this.b,c=this.l,d,f=new (w?Uint8Array:Array)(this.t+(this.a-32768)),h,k,e,g;if(0===c.length)return w?this.b.subarray(32768,this.a):this.b.slice(32768,this.a);h=0;for(k=c.length;h<k;++h){d=c[h];e=0;for(g=d.length;e<g;++e)f[a++]=d[e]}h=32768;for(k=this.a;h<k;++h)f[a++]=b[h];this.l=[];return this.buffer=f};
  u.S=function(){var a,b=this.a;w?this.L?(a=new Uint8Array(b),a.set(this.b.subarray(0,b))):a=this.b.subarray(0,b):(this.b.length>b&&(this.b.length=b),a=this.b);return this.buffer=a};function V(a){a=a||{};this.files=[];this.v=a.comment}V.prototype.M=function(a){this.j=a};V.prototype.s=function(a){var b=a[2]&65535|2;return b*(b^1)>>8&255};V.prototype.k=function(a,b){a[0]=(B[(a[0]^b)&255]^a[0]>>>8)>>>0;a[1]=(6681*(20173*(a[1]+(a[0]&255))>>>0)>>>0)+1>>>0;a[2]=(B[(a[2]^a[1]>>>24)&255]^a[2]>>>8)>>>0};V.prototype.U=function(a){var b=[305419896,591751049,878082192],c,d;w&&(b=new Uint32Array(b));c=0;for(d=a.length;c<d;++c)this.k(b,a[c]&255);return b};function W(a,b){b=b||{};this.input=w&&a instanceof Array?new Uint8Array(a):a;this.c=0;this.ca=b.verify||!1;this.j=b.password}var na={P:0,N:8},X=[80,75,1,2],Y=[80,75,3,4],Z=[80,75,5,6];function oa(a,b){this.input=a;this.offset=b}
  oa.prototype.parse=function(){var a=this.input,b=this.offset;(a[b++]!==X[0]||a[b++]!==X[1]||a[b++]!==X[2]||a[b++]!==X[3])&&m(Error("invalid file header signature"));this.version=a[b++];this.ja=a[b++];this.$=a[b++]|a[b++]<<8;this.I=a[b++]|a[b++]<<8;this.A=a[b++]|a[b++]<<8;this.time=a[b++]|a[b++]<<8;this.V=a[b++]|a[b++]<<8;this.p=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<<
  8;this.g=a[b++]|a[b++]<<8;this.F=a[b++]|a[b++]<<8;this.fa=a[b++]|a[b++]<<8;this.ha=a[b++]|a[b++]<<8;this.ga=a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24;this.aa=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.filename=String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.v=w?a.subarray(b,b+this.F):a.slice(b,b+this.F);this.length=b-this.offset};function pa(a,b){this.input=a;this.offset=b}var qa={O:1,da:8,ea:2048};
  pa.prototype.parse=function(){var a=this.input,b=this.offset;(a[b++]!==Y[0]||a[b++]!==Y[1]||a[b++]!==Y[2]||a[b++]!==Y[3])&&m(Error("invalid local file header signature"));this.$=a[b++]|a[b++]<<8;this.I=a[b++]|a[b++]<<8;this.A=a[b++]|a[b++]<<8;this.time=a[b++]|a[b++]<<8;this.V=a[b++]|a[b++]<<8;this.p=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.z=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.J=(a[b++]|a[b++]<<8|a[b++]<<16|a[b++]<<24)>>>0;this.h=a[b++]|a[b++]<<8;this.g=a[b++]|a[b++]<<8;this.filename=
  String.fromCharCode.apply(null,w?a.subarray(b,b+=this.h):a.slice(b,b+=this.h));this.Y=w?a.subarray(b,b+=this.g):a.slice(b,b+=this.g);this.length=b-this.offset};
  function $(a){var b=[],c={},d,f,h,k;if(!a.i){if(a.o===q){var e=a.input,g;if(!a.D)a:{var l=a.input,p;for(p=l.length-12;0<p;--p)if(l[p]===Z[0]&&l[p+1]===Z[1]&&l[p+2]===Z[2]&&l[p+3]===Z[3]){a.D=p;break a}m(Error("End of Central Directory Record not found"))}g=a.D;(e[g++]!==Z[0]||e[g++]!==Z[1]||e[g++]!==Z[2]||e[g++]!==Z[3])&&m(Error("invalid signature"));a.ia=e[g++]|e[g++]<<8;a.ka=e[g++]|e[g++]<<8;a.la=e[g++]|e[g++]<<8;a.ba=e[g++]|e[g++]<<8;a.R=(e[g++]|e[g++]<<8|e[g++]<<16|e[g++]<<24)>>>0;a.o=(e[g++]|
  e[g++]<<8|e[g++]<<16|e[g++]<<24)>>>0;a.w=e[g++]|e[g++]<<8;a.v=w?e.subarray(g,g+a.w):e.slice(g,g+a.w)}d=a.o;h=0;for(k=a.ba;h<k;++h)f=new oa(a.input,d),f.parse(),d+=f.length,b[h]=f,c[f.filename]=h;a.R<d-a.o&&m(Error("invalid file header size"));a.i=b;a.G=c}}u=W.prototype;u.Z=function(){var a=[],b,c,d;this.i||$(this);d=this.i;b=0;for(c=d.length;b<c;++b)a[b]=d[b].filename;return a};
  u.r=function(a,b){var c;this.G||$(this);c=this.G[a];c===q&&m(Error(a+" not found"));var d;d=b||{};var f=this.input,h=this.i,k,e,g,l,p,s,r,A;h||$(this);h[c]===q&&m(Error("wrong index"));e=h[c].aa;k=new pa(this.input,e);k.parse();e+=k.length;g=k.z;if(0!==(k.I&qa.O)){!d.password&&!this.j&&m(Error("please set password"));s=this.T(d.password||this.j);r=e;for(A=e+12;r<A;++r)ra(this,s,f[r]);e+=12;g-=12;r=e;for(A=e+g;r<A;++r)f[r]=ra(this,s,f[r])}switch(k.A){case na.P:l=w?this.input.subarray(e,e+g):this.input.slice(e,
  e+g);break;case na.N:l=(new F(this.input,{index:e,bufferSize:k.J})).r();break;default:m(Error("unknown compression type"))}if(this.ca){var t=q,n,N="number"===typeof t?t:t=0,ka=l.length;n=-1;for(N=ka&7;N--;++t)n=n>>>8^B[(n^l[t])&255];for(N=ka>>3;N--;t+=8)n=n>>>8^B[(n^l[t])&255],n=n>>>8^B[(n^l[t+1])&255],n=n>>>8^B[(n^l[t+2])&255],n=n>>>8^B[(n^l[t+3])&255],n=n>>>8^B[(n^l[t+4])&255],n=n>>>8^B[(n^l[t+5])&255],n=n>>>8^B[(n^l[t+6])&255],n=n>>>8^B[(n^l[t+7])&255];p=(n^4294967295)>>>0;k.p!==p&&m(Error("wrong crc: file=0x"+
  k.p.toString(16)+", data=0x"+p.toString(16)))}return l};u.M=function(a){this.j=a};function ra(a,b,c){c^=a.s(b);a.k(b,c);return c}u.k=V.prototype.k;u.T=V.prototype.U;u.s=V.prototype.s;v("Zlib.Unzip",W);v("Zlib.Unzip.prototype.decompress",W.prototype.r);v("Zlib.Unzip.prototype.getFilenames",W.prototype.Z);v("Zlib.Unzip.prototype.setPassword",W.prototype.M);}).call(this);
  
  /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function n(e){throw e;}var q=void 0,aa=this;function r(e,c){var d=e.split("."),b=aa;!(d[0]in b)&&b.execScript&&b.execScript("var "+d[0]);for(var a;d.length&&(a=d.shift());)!d.length&&c!==q?b[a]=c:b=b[a]?b[a]:b[a]={}};var u="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;new (u?Uint8Array:Array)(256);var v;for(v=0;256>v;++v)for(var w=v,ba=7,w=w>>>1;w;w>>>=1)--ba;function x(e,c,d){var b,a="number"===typeof c?c:c=0,f="number"===typeof d?d:e.length;b=-1;for(a=f&7;a--;++c)b=b>>>8^z[(b^e[c])&255];for(a=f>>3;a--;c+=8)b=b>>>8^z[(b^e[c])&255],b=b>>>8^z[(b^e[c+1])&255],b=b>>>8^z[(b^e[c+2])&255],b=b>>>8^z[(b^e[c+3])&255],b=b>>>8^z[(b^e[c+4])&255],b=b>>>8^z[(b^e[c+5])&255],b=b>>>8^z[(b^e[c+6])&255],b=b>>>8^z[(b^e[c+7])&255];return(b^4294967295)>>>0}
  var A=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,
  2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,
  2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,
  2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,
  3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,
  936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],z=u?new Uint32Array(A):A;function B(){}B.prototype.getName=function(){return this.name};B.prototype.getData=function(){return this.data};B.prototype.H=function(){return this.I};r("Zlib.GunzipMember",B);r("Zlib.GunzipMember.prototype.getName",B.prototype.getName);r("Zlib.GunzipMember.prototype.getData",B.prototype.getData);r("Zlib.GunzipMember.prototype.getMtime",B.prototype.H);function D(e){var c=e.length,d=0,b=Number.POSITIVE_INFINITY,a,f,g,k,m,p,t,h,l,y;for(h=0;h<c;++h)e[h]>d&&(d=e[h]),e[h]<b&&(b=e[h]);a=1<<d;f=new (u?Uint32Array:Array)(a);g=1;k=0;for(m=2;g<=d;){for(h=0;h<c;++h)if(e[h]===g){p=0;t=k;for(l=0;l<g;++l)p=p<<1|t&1,t>>=1;y=g<<16|h;for(l=p;l<a;l+=m)f[l]=y;++k}++g;k<<=1;m<<=1}return[f,d,b]};var E=[],F;for(F=0;288>F;F++)switch(!0){case 143>=F:E.push([F+48,8]);break;case 255>=F:E.push([F-144+400,9]);break;case 279>=F:E.push([F-256+0,7]);break;case 287>=F:E.push([F-280+192,8]);break;default:n("invalid literal: "+F)}
  var ca=function(){function e(a){switch(!0){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272,
  a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:n("invalid length: "+a)}}var c=[],d,b;for(d=3;258>=d;d++)b=e(d),c[d]=b[2]<<24|b[1]<<
  16|b[0];return c}();u&&new Uint32Array(ca);function G(e,c){this.i=[];this.j=32768;this.d=this.f=this.c=this.n=0;this.input=u?new Uint8Array(e):e;this.o=!1;this.k=H;this.z=!1;if(c||!(c={}))c.index&&(this.c=c.index),c.bufferSize&&(this.j=c.bufferSize),c.bufferType&&(this.k=c.bufferType),c.resize&&(this.z=c.resize);switch(this.k){case I:this.a=32768;this.b=new (u?Uint8Array:Array)(32768+this.j+258);break;case H:this.a=0;this.b=new (u?Uint8Array:Array)(this.j);this.e=this.F;this.q=this.B;this.l=this.D;break;default:n(Error("invalid inflate mode"))}}
  var I=0,H=1;
  G.prototype.g=function(){for(;!this.o;){var e=J(this,3);e&1&&(this.o=!0);e>>>=1;switch(e){case 0:var c=this.input,d=this.c,b=this.b,a=this.a,f=c.length,g=q,k=q,m=b.length,p=q;this.d=this.f=0;d+1>=f&&n(Error("invalid uncompressed block header: LEN"));g=c[d++]|c[d++]<<8;d+1>=f&&n(Error("invalid uncompressed block header: NLEN"));k=c[d++]|c[d++]<<8;g===~k&&n(Error("invalid uncompressed block header: length verify"));d+g>c.length&&n(Error("input buffer is broken"));switch(this.k){case I:for(;a+g>b.length;){p=
  m-a;g-=p;if(u)b.set(c.subarray(d,d+p),a),a+=p,d+=p;else for(;p--;)b[a++]=c[d++];this.a=a;b=this.e();a=this.a}break;case H:for(;a+g>b.length;)b=this.e({t:2});break;default:n(Error("invalid inflate mode"))}if(u)b.set(c.subarray(d,d+g),a),a+=g,d+=g;else for(;g--;)b[a++]=c[d++];this.c=d;this.a=a;this.b=b;break;case 1:this.l(da,ea);break;case 2:fa(this);break;default:n(Error("unknown BTYPE: "+e))}}return this.q()};
  var K=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],L=u?new Uint16Array(K):K,N=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],O=u?new Uint16Array(N):N,P=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],Q=u?new Uint8Array(P):P,R=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ga=u?new Uint16Array(R):R,ha=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,
  13,13],U=u?new Uint8Array(ha):ha,V=new (u?Uint8Array:Array)(288),W,ia;W=0;for(ia=V.length;W<ia;++W)V[W]=143>=W?8:255>=W?9:279>=W?7:8;var da=D(V),X=new (u?Uint8Array:Array)(30),Y,ja;Y=0;for(ja=X.length;Y<ja;++Y)X[Y]=5;var ea=D(X);function J(e,c){for(var d=e.f,b=e.d,a=e.input,f=e.c,g=a.length,k;b<c;)f>=g&&n(Error("input buffer is broken")),d|=a[f++]<<b,b+=8;k=d&(1<<c)-1;e.f=d>>>c;e.d=b-c;e.c=f;return k}
  function Z(e,c){for(var d=e.f,b=e.d,a=e.input,f=e.c,g=a.length,k=c[0],m=c[1],p,t;b<m&&!(f>=g);)d|=a[f++]<<b,b+=8;p=k[d&(1<<m)-1];t=p>>>16;e.f=d>>t;e.d=b-t;e.c=f;return p&65535}
  function fa(e){function c(a,c,b){var d,e=this.w,f,g;for(g=0;g<a;)switch(d=Z(this,c),d){case 16:for(f=3+J(this,2);f--;)b[g++]=e;break;case 17:for(f=3+J(this,3);f--;)b[g++]=0;e=0;break;case 18:for(f=11+J(this,7);f--;)b[g++]=0;e=0;break;default:e=b[g++]=d}this.w=e;return b}var d=J(e,5)+257,b=J(e,5)+1,a=J(e,4)+4,f=new (u?Uint8Array:Array)(L.length),g,k,m,p;for(p=0;p<a;++p)f[L[p]]=J(e,3);if(!u){p=a;for(a=f.length;p<a;++p)f[L[p]]=0}g=D(f);k=new (u?Uint8Array:Array)(d);m=new (u?Uint8Array:Array)(b);e.w=
  0;e.l(D(c.call(e,d,g,k)),D(c.call(e,b,g,m)))}G.prototype.l=function(e,c){var d=this.b,b=this.a;this.r=e;for(var a=d.length-258,f,g,k,m;256!==(f=Z(this,e));)if(256>f)b>=a&&(this.a=b,d=this.e(),b=this.a),d[b++]=f;else{g=f-257;m=O[g];0<Q[g]&&(m+=J(this,Q[g]));f=Z(this,c);k=ga[f];0<U[f]&&(k+=J(this,U[f]));b>=a&&(this.a=b,d=this.e(),b=this.a);for(;m--;)d[b]=d[b++-k]}for(;8<=this.d;)this.d-=8,this.c--;this.a=b};
  G.prototype.D=function(e,c){var d=this.b,b=this.a;this.r=e;for(var a=d.length,f,g,k,m;256!==(f=Z(this,e));)if(256>f)b>=a&&(d=this.e(),a=d.length),d[b++]=f;else{g=f-257;m=O[g];0<Q[g]&&(m+=J(this,Q[g]));f=Z(this,c);k=ga[f];0<U[f]&&(k+=J(this,U[f]));b+m>a&&(d=this.e(),a=d.length);for(;m--;)d[b]=d[b++-k]}for(;8<=this.d;)this.d-=8,this.c--;this.a=b};
  G.prototype.e=function(){var e=new (u?Uint8Array:Array)(this.a-32768),c=this.a-32768,d,b,a=this.b;if(u)e.set(a.subarray(32768,e.length));else{d=0;for(b=e.length;d<b;++d)e[d]=a[d+32768]}this.i.push(e);this.n+=e.length;if(u)a.set(a.subarray(c,c+32768));else for(d=0;32768>d;++d)a[d]=a[c+d];this.a=32768;return a};
  G.prototype.F=function(e){var c,d=this.input.length/this.c+1|0,b,a,f,g=this.input,k=this.b;e&&("number"===typeof e.t&&(d=e.t),"number"===typeof e.A&&(d+=e.A));2>d?(b=(g.length-this.c)/this.r[2],f=258*(b/2)|0,a=f<k.length?k.length+f:k.length<<1):a=k.length*d;u?(c=new Uint8Array(a),c.set(k)):c=k;return this.b=c};
  G.prototype.q=function(){var e=0,c=this.b,d=this.i,b,a=new (u?Uint8Array:Array)(this.n+(this.a-32768)),f,g,k,m;if(0===d.length)return u?this.b.subarray(32768,this.a):this.b.slice(32768,this.a);f=0;for(g=d.length;f<g;++f){b=d[f];k=0;for(m=b.length;k<m;++k)a[e++]=b[k]}f=32768;for(g=this.a;f<g;++f)a[e++]=c[f];this.i=[];return this.buffer=a};
  G.prototype.B=function(){var e,c=this.a;u?this.z?(e=new Uint8Array(c),e.set(this.b.subarray(0,c))):e=this.b.subarray(0,c):(this.b.length>c&&(this.b.length=c),e=this.b);return this.buffer=e};function $(e){this.input=e;this.c=0;this.m=[];this.s=!1}$.prototype.G=function(){this.s||this.g();return this.m.slice()};
  $.prototype.g=function(){for(var e=this.input.length;this.c<e;){var c=new B,d=q,b=q,a=q,f=q,g=q,k=q,m=q,p=q,t=q,h=this.input,l=this.c;c.u=h[l++];c.v=h[l++];(31!==c.u||139!==c.v)&&n(Error("invalid file signature:"+c.u+","+c.v));c.p=h[l++];switch(c.p){case 8:break;default:n(Error("unknown compression method: "+c.p))}c.h=h[l++];p=h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24;c.I=new Date(1E3*p);c.O=h[l++];c.N=h[l++];0<(c.h&4)&&(c.J=h[l++]|h[l++]<<8,l+=c.J);if(0<(c.h&8)){m=[];for(k=0;0<(g=h[l++]);)m[k++]=String.fromCharCode(g);
  c.name=m.join("")}if(0<(c.h&16)){m=[];for(k=0;0<(g=h[l++]);)m[k++]=String.fromCharCode(g);c.K=m.join("")}0<(c.h&2)&&(c.C=x(h,0,l)&65535,c.C!==(h[l++]|h[l++]<<8)&&n(Error("invalid header crc16")));d=h[h.length-4]|h[h.length-3]<<8|h[h.length-2]<<16|h[h.length-1]<<24;h.length-l-4-4<512*d&&(f=d);b=new G(h,{index:l,bufferSize:f});c.data=a=b.g();l=b.c;c.L=t=(h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24)>>>0;x(a,q,q)!==t&&n(Error("invalid CRC-32 checksum: 0x"+x(a,q,q).toString(16)+" / 0x"+t.toString(16)));c.M=
  d=(h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24)>>>0;(a.length&4294967295)!==d&&n(Error("invalid input size: "+(a.length&4294967295)+" / "+d));this.m.push(c);this.c=l}this.s=!0;var y=this.m,s,M,S=0,T=0,C;s=0;for(M=y.length;s<M;++s)T+=y[s].data.length;if(u){C=new Uint8Array(T);for(s=0;s<M;++s)C.set(y[s].data,S),S+=y[s].data.length}else{C=[];for(s=0;s<M;++s)C[s]=y[s].data;C=Array.prototype.concat.apply([],C)}return C};r("Zlib.Gunzip",$);r("Zlib.Gunzip.prototype.decompress",$.prototype.g);r("Zlib.Gunzip.prototype.getMembers",$.prototype.G);}).call(this);
  
  }).bind(elFinder)();
  
  } catch(e) {};
  
  /*
   * File: /js/commands/reload.js
   */
  
  /**
   * @class  elFinder command "reload"
   * Sync files and folders
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.reload = function() {
  	var self   = this,
  		search = false;
  	
  	this.alwaysEnabled = true;
  	this.updateOnSelect = true;
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+shift+r f5'
  	}];
  	
  	this.getstate = function() {
  		return 0;
  	};
  	
  	this.init = function() {
  		this.fm.bind('search searchend', function(e) {
  			search = e.type == 'search';
  		});
  	};
  	
  	this.fm.bind('contextmenu', function(e){
  		var fm = self.fm;
  		if (fm.options.sync >= 1000) {
  			self.extra = {
  				icon: 'accept',
  				node: $('<span/>')
  					.attr({title: fm.i18n('autoSync')})
  					.on('click touchstart', function(e){
  						if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  							return;
  						}
  						e.stopPropagation();
  						e.preventDefault();
  						$(this).parent()
  							.toggleClass('ui-state-disabled', fm.options.syncStart)
  							.parent().removeClass('ui-state-hover');
  						fm.options.syncStart = !fm.options.syncStart;
  						fm.autoSync(fm.options.syncStart? null : 'stop');
  					}).on('ready', function(){
  						$(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto');
  					})
  			};
  		}
  	});
  	
  	this.exec = function() {
  		var fm = this.fm;
  		if (!search) {
  			var dfrd    = fm.sync(),
  				timeout = setTimeout(function() {
  					fm.notify({type : 'reload', cnt : 1, hideCnt : true});
  					dfrd.always(function() { fm.notify({type : 'reload', cnt  : -1}); });
  				}, fm.notifyDelay);
  				
  			return dfrd.always(function() { 
  				clearTimeout(timeout); 
  				fm.trigger('reload');
  			});
  		} else {
  			$('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click();
  		}
  	};
  
  };
  
  
  /*
   * File: /js/commands/rename.js
   */
  
  /**
   * @class elFinder command "rename". 
   * Rename selected file.
   *
   * @author Dmitry (dio) Levashov, dio@std42.ru
   **/
  elFinder.prototype.commands.rename = function() {
  	
  	this.shortcuts = [{
  		pattern     : 'f2'+(this.fm.OS == 'mac' ? ' enter' : '')
  	}];
  	
  	this.getstate = function(sel) {
  		var sel = this.files(sel);
  
  		return !this._disabled && sel.length == 1 && sel[0].phash && !sel[0].locked  ? 0 : -1;
  	};
  	
  	this.exec = function(hashes, opts) {
  		var fm       = this.fm,
  			cwd      = fm.getUI('cwd'),
  			sel      = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
  			cnt      = sel.length,
  			file     = fm.file(sel.shift()),
  			filename = '.elfinder-cwd-filename',
  			opts     = opts || {},
  			incwd    = (fm.cwd().hash == file.hash),
  			type     = opts._currentType? opts._currentType : (incwd? 'navbar' : 'files'),
  			navbar   = (type === 'navbar'),
  			target   = $('#'+fm[navbar? 'navHash2Id' : 'cwdHash2Id'](file.hash)),
  			tarea    = (type === 'files' && fm.storage('view') != 'list'),
  			rest     = function(){
  				if (!overlay.is(':hidden')) {
  					overlay.addClass('ui-front')
  						.elfinderoverlay('hide')
  						.off('click', cancel);
  				}
  				pnode.removeClass('ui-front').css('position', '');
  				if (tarea) {
  					node.css('max-height', '');
  				} else if (!navbar) {
  					pnode.css('width', '')
  						.parent('td').css('overflow', '');
  				}
  			}, colwidth,
  			dfrd     = $.Deferred()
  				.done(function(data){
  					incwd && fm.exec('open', data.added[0].hash);
  				})
  				.fail(function(error) {
  					var parent = input.parent(),
  						name   = fm.escape(file.name);
  
  					if (tarea) {
  						name = name.replace(/([_.])/g, '&#8203;$1');
  					}
  					if (navbar) {
  						input.replaceWith(name);
  					} else {
  						if (parent.length) {
  							input.remove();
  							parent.html(name);
  						} else {
  							//cwd.find('#'+fm.cwdHash2Id(file.hash)).find(filename).html(name);
  							target.find(filename).html(name);
  							setTimeout(function() {
  								cwd.find('#'+fm.cwdHash2Id(file.hash)).click();
  							}, 50);
  						}
  					}
  					
  					error && fm.error(error);
  				})
  				.always(function() {
  					rest();
  					fm.unbind('resize', resize);
  					fm.enable();
  				}),
  			input = $(tarea? '<textarea/>' : '<input type="text"/>')
  				.on('keyup text', function(){
  					if (tarea) {
  						this.style.height = '1px';
  						this.style.height = this.scrollHeight + 'px';
  					} else if (colwidth) {
  						this.style.width = colwidth + 'px';
  						if (this.scrollWidth > colwidth) {
  							this.style.width = this.scrollWidth + 10 + 'px';
  						}
  					}
  				})
  				.keydown(function(e) {
  					e.stopImmediatePropagation();
  					if (e.keyCode == $.ui.keyCode.ESCAPE) {
  						dfrd.reject();
  					} else if (e.keyCode == $.ui.keyCode.ENTER) {
  						e.preventDefault();
  						input.blur();
  					}
  				})
  				.mousedown(function(e) {
  					e.stopPropagation();
  				})
  				.click(function(e) { // for touch device
  					e.stopPropagation();
  				})
  				.dblclick(function(e) {
  					e.stopPropagation();
  					e.preventDefault();
  				})
  				.blur(function() {
  					var name   = $.trim(input.val()),
  						parent = input.parent(),
  						valid  = true;
  
  					if (!inError && pnode.length) {
  						if (input[0].setSelectionRange) {
  							input[0].setSelectionRange(0, 0)
  						}
  						if (name == file.name) {
  							return dfrd.reject();
  						}
  						if (fm.options.validName && fm.options.validName.test) {
  							try {
  								valid = fm.options.validName.test(name);
  							} catch(e) {
  								valid = false;
  							}
  						}
  						if (!name || name === '..' || !valid) {
  							inError = true;
  							fm.error('errInvName', {modal: true, close: select});
  							return false;
  						}
  						if (fm.fileByName(name, file.phash)) {
  							inError = true;
  							fm.error(['errExists', name], {modal: true, close: select});
  							return false;
  						}
  						
  						rest();
  						(navbar? pnode : node).html(fm.escape(name));
  						fm.lockfiles({files : [file.hash]});
  						fm.request({
  								data   : {cmd : 'rename', target : file.hash, name : name},
  								notify : {type : 'rename', cnt : 1}
  							})
  							.fail(function(error) {
  								dfrd.reject();
  								fm.sync();
  							})
  							.done(function(data) {
  								dfrd.resolve(data);
  								if (!navbar && data && data.added && data.added[0]) {
  									var newItem = cwd.find('#'+fm.cwdHash2Id(data.added[0].hash));
  									if (newItem.length) {
  										newItem.trigger('scrolltoview');
  									}
  								}
  							})
  							.always(function() {
  								fm.unlockfiles({files : [file.hash]})
  							});
  						
  					}
  				}),
  			select = function() {
  				var name = input.val().replace(/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/ig, '');
  				inError = false;
  				if (fm.UA.Mobile) {
  					overlay.on('click', cancel)
  						.removeClass('ui-front').elfinderoverlay('show');
  				}
  				input.select().focus();
  				input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  			},
  			node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); })
  					: target.find(filename),
  			pnode = node.parent(),
  			overlay = fm.getUI().children('.elfinder-overlay'),
  			cancel = function(e) { 
  				if (! inError) {
  					e.stopPropagation();
  					dfrd.reject();
  				}
  			},
  			resize = function() {
  				target.trigger('scrolltoview');
  			},
  			inError = false;
  		
  		pnode.addClass('ui-front').css('position', 'relative');
  		fm.bind('resize', resize);
  		if (navbar) {
  			node.replaceWith(input.val(file.name));
  		} else {
  			if (tarea) {
  				node.css('max-height', 'none');
  			} else if (!navbar) {
  				colwidth = pnode.width();
  				pnode.width(colwidth - 15)
  					.parent('td').css('overflow', 'visible');
  			}
  			node.empty().append(input.val(file.name));
  		}
  		
  		if (cnt > 1 || this.getstate([file.hash]) < 0) {
  			return dfrd.reject();
  		}
  		
  		if (!file || !node.length) {
  			return dfrd.reject('errCmdParams', this.title);
  		}
  		
  		if (file.locked) {
  			return dfrd.reject(['errLocked', file.name]);
  		}
  		
  		fm.one('select', function() {
  			input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.blur();
  		})
  		
  		input.trigger('keyup');
  		
  		select();
  		
  		return dfrd;
  	};
  
  };
  
  
  /*
   * File: /js/commands/resize.js
   */
  
  /**
   * @class  elFinder command "resize"
   * Open dialog to resize image
   *
   * @author Dmitry (dio) Levashov
   * @author Alexey Sukhotin
   * @author Naoki Sawada
   * @author Sergio Jovani
   **/
  elFinder.prototype.commands.resize = function() {
  
  	this.updateOnSelect = false;
  	
  	this.getstate = function() {
  		var sel = this.fm.selectedFiles();
  		return !this._disabled && sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1;
  	};
  	
  	this.exec = function(hashes) {
  		var fm    = this.fm,
  			files = this.files(hashes),
  			dfrd  = $.Deferred(),
  			api2  = (fm.api > 1),
  			dialogWidth = 650,
  			
  			open = function(file, id) {
  				var isJpeg   = (file.mime === 'image/jpeg'),
  					dialog   = $('<div class="elfinder-dialog-resize '+fm.res('class', 'editing')+'"/>'),
  					input    = '<input type="text" size="5"/>',
  					row      = '<div class="elfinder-resize-row"/>',
  					label    = '<div class="elfinder-resize-label"/>',
  					control  = $('<div class="elfinder-resize-control"/>'),
  					preview  = $('<div class="ui-front elfinder-resize-preview"/>')
  						.on('touchmove', function(e) {
  							e.stopPropagation();
  							e.preventDefault();
  						}),
  					spinner  = $('<div class="elfinder-resize-spinner">'+fm.i18n('ntfloadimg')+'</div>'),
  					rhandle  = $('<div class="elfinder-resize-handle touch-punch"/>'),
  					rhandlec = $('<div class="elfinder-resize-handle touch-punch"/>'),
  					uiresize = $('<div class="elfinder-resize-uiresize"/>'),
  					uicrop   = $('<div class="elfinder-resize-uicrop"/>'),
  					uibuttonset = '<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>',
  					uibutton    = '<div class="ui-state-default elfinder-button"/>',
  					uiseparator = '<span class="ui-widget-content elfinder-toolbar-button-separator"/>',
  					uirotate    = $('<div class="elfinder-resize-rotate"/>'),
  					uideg270    = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>')
  						.click(function(){
  							rdegree = rdegree - 90;
  							rotate.update(rdegree);
  						})),
  					uideg90     = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>')
  						.click(function(){
  							rdegree = rdegree + 90;
  							rotate.update(rdegree);
  						})),
  					uiprop   = $('<span />'),
  					reset    = $('<div class="ui-state-default ui-corner-all elfinder-resize-reset"><span class="ui-icon ui-icon-arrowreturnthick-1-w"/></div>'),
  					uitype   = $('<div class="elfinder-resize-type"/>')
  						.append('<input class="" type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>',
  						'<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>',
  						'<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'),
  					type     = $('input', uitype).attr('disabled', 'disabled')
  						.change(function() {
  							var val = $('input:checked', uitype).val();
  							
  							resetView();
  							resizable(true);
  							croppable(true);
  							rotateable(true);
  							
  							if (val == 'resize') {
  								uiresize.show();
  								uirotate.hide();
  								uicrop.hide();
  								resizable();
  							}
  							else if (val == 'crop') {
  								uirotate.hide();
  								uiresize.hide();
  								uicrop.show();
  								croppable();
  							} else if (val == 'rotate') {
  								uiresize.hide();
  								uicrop.hide();
  								uirotate.show();
  								rotateable();
  							}
  						}),
  					constr  = $('<input type="checkbox" checked="checked"/>')
  						.change(function() {
  							cratio = !!constr.prop('checked');
  							resize.fixHeight();
  							resizable(true);
  							resizable();
  						}),
  					width   = $(input)
  						.change(function() {
  							var w = parseInt(width.val()),
  								h = parseInt(cratio ? Math.round(w/ratio) : height.val());
  
  							if (w > 0 && h > 0) {
  								resize.updateView(w, h);
  								height.val(h);
  							}
  						}),
  					height  = $(input)
  						.change(function() {
  							var h = parseInt(height.val()),
  								w = parseInt(cratio ? Math.round(h*ratio) : width.val());
  
  							if (w > 0 && h > 0) {
  								resize.updateView(w, h);
  								width.val(w);
  							}
  						}),
  					pointX  = $(input).change(function(){crop.updateView();}),
  					pointY  = $(input).change(function(){crop.updateView();}),
  					offsetX = $(input).change(function(){crop.updateView();}),
  					offsetY = $(input).change(function(){crop.updateView();}),
  					quality = isJpeg && api2?
  						$(input).val(fm.option('jpgQuality'))
  							.addClass('quality')
  							.on('blur', function(){
  								var q = Math.min(100, Math.max(1, parseInt(this.value)));
  								dialog.find('input.quality').val(q);
  							})
  						: null,
  					degree = $('<input type="text" size="3" maxlength="3" value="0" />')
  						.change(function() {
  							rotate.update();
  						}),
  					uidegslider = $('<div class="elfinder-resize-rotate-slider"/>')
  						.addClass('touch-punch')
  						.slider({
  							min: 0,
  							max: 360,
  							value: degree.val(),
  							animate: true,
  							change: function(event, ui) {
  								if (ui.value != uidegslider.slider('value')) {
  									rotate.update(ui.value);
  								}
  							},
  							slide: function(event, ui) {
  								rotate.update(ui.value, false);
  							}
  						}),
  					ratio   = 1,
  					prop    = 1,
  					owidth  = 0,
  					oheight = 0,
  					cratio  = true,
  					pwidth  = 0,
  					pheight = 0,
  					rwidth  = 0,
  					rheight = 0,
  					rdegree = 0,
  					setuprimg = function() {
  						var r_scale;
  						r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2));
  						rwidth = owidth * r_scale;
  						rheight = oheight * r_scale;
  						imgr.width(rwidth)
  							.height(rheight)
  							.css('margin-top', (pheight-rheight)/2 + 'px')
  							.css('margin-left', (pwidth-rwidth)/2 + 'px');
  					},
  					setupimg = function() {
  
  						resize.updateView(owidth, oheight);
  
  						setuprimg();
  
  						basec
  							.width(img.width())
  							.height(img.height());
  						imgc
  							.width(img.width())
  							.height(img.height());
  						coverc
  							.width(img.width())
  							.height(img.height());
  						rhandlec
  							.width(imgc.width())
  							.height(imgc.height())
  							.offset(imgc.offset());
  					},
  					img     = $('<img/>')
  						.on('load', function() {
  							var r_scale, inputFirst,
  								imgRatio = img.height() / img.width();
  							
  							if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) {
  								preview.height(preview.width() * imgRatio);
  							}
  							
  							if (preview.height() > img.height() + 20) {
  								preview.height(img.height() + 20);
  							}
  							
  							pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  							
  							spinner.remove();
  							
  							owidth  = img.width();
  							oheight = img.height();
  							ratio   = owidth/oheight;
  
  							rhandle.append(img.show()).show();
  							width.val(owidth);
  							height.val(oheight);
  
  							setupimg();
  							
  							type.button('enable');
  							inputFirst = control.find('input,select').removeAttr('disabled')
  								.filter(':text').keydown(function(e) {
  									var c = e.keyCode, i;
  
  									e.stopPropagation();
  								
  									if ((c >= 37 && c <= 40) 
  									|| c == $.ui.keyCode.BACKSPACE 
  									|| c == $.ui.keyCode.DELETE 
  									|| (c == 65 && (e.ctrlKey||e.metaKey))
  									|| c == 27) {
  										return;
  									}
  								
  									if (c == 9) {
  										i = $(this).parent()[e.shiftKey ? 'prevAll' : 'nextAll']('div.elfinder-resize-row').children(':text');
  
  										if (i.length) {
  											i[0].focus();
  										} else {
  											$(this).parent().parent().find(':text:' + (e.shiftKey ? 'last' : 'first')).focus();
  										}
  									}
  								
  									if (c == 13) {
  										fm.confirm({
  											title  : $('input:checked', uitype).val(),
  											text   : 'confirmReq',
  											accept : {
  												label    : 'btnApply',
  												callback : function() {  
  													save();
  												}
  											},
  											cancel : {
  												label    : 'btnCancel',
  												callback : function(){}
  											}
  										});
  										return;
  									}
  								
  									if (!((c >= 48 && c <= 57) || (c >= 96 && c <= 105))) {
  										e.preventDefault();
  									}
  								})
  								.filter(':first');
  								
  							!fm.UA.Mobile && inputFirst.focus();
  							resizable();
  							
  							reset.hover(function() { reset.toggleClass('ui-state-hover'); }).click(resetView);
  							
  						})
  						.on('error', function() {
  							spinner.text('Unable to load image').css('background', 'transparent');
  						}),
  					basec = $('<div/>'),
  					imgc = $('<img/>'),
  					coverc = $('<div/>'),
  					imgr = $('<img/>'),
  					round = function(v) {
  						return isJpeg? Math.round(v/8)*8 : Math.round(v);
  					},
  					resetView = function() {
  						width.val(owidth);
  						height.val(oheight);
  						resize.updateView(owidth, oheight);
  					},
  					resize = {
  						update : function() {
  							width.val(round(img.width()/prop));
  							height.val(round(img.height()/prop));
  						},
  						
  						updateView : function(w, h) {
  							if (w > pwidth || h > pheight) {
  								if (w / pwidth > h / pheight) {
  									prop = pwidth / w;
  									img.width(pwidth).height(Math.ceil(h*prop));
  								} else {
  									prop = pheight / h;
  									img.height(pheight).width(Math.ceil(w*prop));
  								}
  							} else {
  								img.width(w).height(h);
  							}
  							
  							prop = img.width()/w;
  							uiprop.text('1 : '+(1/prop).toFixed(2));
  							resize.updateHandle();
  						},
  						
  						updateHandle : function() {
  							rhandle.width(img.width()).height(img.height());
  						},
  						fixHeight : function() {
  							var w, h;
  							if (cratio) {
  								w = width.val();
  								h = Math.round(w/ratio);
  								resize.updateView(w, h);
  								height.val(h);
  							}
  						}
  					},
  					crop = {
  						update : function() {
  							offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop));
  							offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop));
  							pointX.val(Math.round(((rhandlec.data('x')||rhandlec.offset().left)-imgc.offset().left)/prop));
  							pointY.val(Math.round(((rhandlec.data('y')||rhandlec.offset().top)-imgc.offset().top)/prop));
  						},
  						updateView : function() {
  							var x = parseInt(pointX.val()) * prop + imgc.offset().left;
  							var y = parseInt(pointY.val()) * prop + imgc.offset().top;
  							var w = offsetX.val() * prop;
  							var h = offsetY.val() * prop;
  							rhandlec.data({x: x, y: y, w: w, h: h})
  								.width(Math.round(w))
  								.height(Math.round(h))
  								.offset({left: Math.round(x), top: Math.round(y)});
  							coverc.width(rhandlec.width())
  								.height(rhandlec.height());
  						},
  						resize_update : function() {
  							rhandlec.data({w: null, h: null});
  							crop.update();
  							coverc.width(rhandlec.width())
  								.height(rhandlec.height());
  						},
  						drag_update : function() {
  							rhandlec.data({x: null, y: null});
  							crop.update();
  						}
  					},
  					rotate = {
  						mouseStartAngle : 0,
  						imageStartAngle : 0,
  						imageBeingRotated : false,
  							
  						update : function(value, animate) {
  							if (typeof value == 'undefined') {
  								rdegree = value = parseInt(degree.val());
  							}
  							if (typeof animate == 'undefined') {
  								animate = true;
  							}
  							if (! animate || fm.UA.Opera || fm.UA.ltIE8) {
  								imgr.rotate(value);
  							} else {
  								imgr.animate({rotate: value + 'deg'});
  							}
  							value = value % 360;
  							if (value < 0) {
  								value += 360;
  							}
  							degree.val(parseInt(value));
  
  							uidegslider.slider('value', degree.val());
  						},
  						
  						execute : function ( e ) {
  							
  							if ( !rotate.imageBeingRotated ) return;
  							
  							var imageCentre = rotate.getCenter( imgr );
  							var mouseXFromCentre = e.pageX - imageCentre[0];
  							var mouseYFromCentre = e.pageY - imageCentre[1];
  							var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre );
  							
  							var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle;
  							rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI);
  							
  							if ( e.shiftKey ) {
  								rotateAngle = Math.round((rotateAngle + 6)/15) * 15;
  							}
  							
  							imgr.rotate(rotateAngle);
  							
  							rotateAngle = rotateAngle % 360;
  							if (rotateAngle < 0) {
  								rotateAngle += 360;
  							}
  							degree.val(rotateAngle);
  
  							uidegslider.slider('value', degree.val());
  							
  							return false;
  						},
  						
  						start : function ( e ) {
  							
  							rotate.imageBeingRotated = true;
  							
  							var imageCentre = rotate.getCenter( imgr );
  							var mouseStartXFromCentre = e.pageX - imageCentre[0];
  							var mouseStartYFromCentre = e.pageY - imageCentre[1];
  							rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre );
  							
  							rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0;
  							
  							$(document).mousemove( rotate.execute );
  							
  							return false;
  						},
  							
  						stop : function ( e ) {
  							
  							if ( !rotate.imageBeingRotated ) return;
  							
  							$(document).unbind( 'mousemove' , rotate.execute);
  							
  							setTimeout( function() { rotate.imageBeingRotated = false; }, 10 );
  							return false;
  						},
  						
  						getCenter : function ( image ) {
  							
  							var currentRotation = imgr.rotate();
  							imgr.rotate(0);
  							
  							var imageOffset = imgr.offset();
  							var imageCentreX = imageOffset.left + imgr.width() / 2;
  							var imageCentreY = imageOffset.top + imgr.height() / 2;
  							
  							imgr.rotate(currentRotation);
  							
  							return Array( imageCentreX, imageCentreY );
  						}
  					},
  					resizable = function(destroy) {
  						if ($.fn.resizable) {
  							if (destroy) {
  								rhandle.filter(':ui-resizable').resizable('destroy');
  								rhandle.hide();
  							}
  							else {
  								rhandle.show();
  								rhandle.resizable({
  									alsoResize  : img,
  									aspectRatio : cratio,
  									resize      : resize.update,
  									stop        : resize.fixHeight
  								});
  								dinit();
  							}
  						}
  					},
  					croppable = function(destroy) {
  						if ($.fn.draggable && $.fn.resizable) {
  							if (destroy) {
  								rhandlec.filter(':ui-resizable').resizable('destroy')
  									.filter(':ui-draggable').draggable('destroy');
  								basec.hide();
  							}
  							else {
  								basec.show();
  								
  								rhandlec
  									.resizable({
  										containment : basec,
  										resize      : crop.resize_update,
  										handles     : 'all'
  									})
  									.draggable({
  										handle      : coverc,
  										containment : imgc,
  										drag        : crop.drag_update
  									});
  								
  								dinit();
  								crop.update();
  							}
  						}
  					},
  					rotateable = function(destroy) {
  						if ($.fn.draggable && $.fn.resizable) {
  							if (destroy) {
  								imgr.hide();
  							}
  							else {
  								imgr.show();
  								dinit();
  
  							}
  						}
  					},
  					save = function() {
  						var w, h, x, y, d, q;
  						var mode = $('input:checked', uitype).val();
  						
  						if (mode == 'resize') {
  							w = parseInt(width.val()) || 0;
  							h = parseInt(height.val()) || 0;
  						} else if (mode == 'crop') {
  							w = parseInt(offsetX.val()) || 0;
  							h = parseInt(offsetY.val()) || 0;
  							x = parseInt(pointX.val()) || 0;
  							y = parseInt(pointY.val()) || 0;
  						} else if (mode == 'rotate') {
  							w = owidth;
  							h = oheight;
  							d = parseInt(degree.val()) || 0;
  							if (d < 0 || d > 360) {
  								return fm.error('Invalid rotate degree');
  							}
  							if (d == 0 || d == 360) {
  								return fm.error('Image dose not rotated');
  							}
  						}
  						q = quality? parseInt(quality.val()) : 0;
  						
  						if (mode != 'rotate') {
  
  							if (w <= 0 || h <= 0) {
  								return fm.error('Invalid image size');
  							}
  							
  							if (w == owidth && h == oheight) {
  								return fm.error('Image size not changed');
  							}
  
  						}
  						
  						dialog.elfinderdialog('close');
  						
  						fm.request({
  							data : {
  								cmd    : 'resize',
  								target : file.hash,
  								width  : w,
  								height : h,
  								x      : x,
  								y      : y,
  								degree : d,
  								quality: q,
  								mode   : mode
  							},
  							notify : {type : 'resize', cnt : 1}
  						})
  						.fail(function(error) {
  							dfrd.reject(error);
  						})
  						.done(function() {
  							var reload = function(url) {
  								var ifm;
  								try {
  									ifm = $('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px">')
  										.attr('src', url)
  										.one('load', function() {
  											this.contentDocument.location.reload(true);
  											ifm.one('load', function() {
  												ifm.remove();
  											});
  										})
  										.appendTo('body');
  								} catch(e) {
  									ifm && ifm.remove();
  								}
  							},
  							url = fm.url(file.hash);
  							
  							reload(src);
  							if (url !== src) {
  								reload(url);
  							}
  							dfrd.resolve();
  						});
  						
  					},
  					buttons = {},
  					hline   = 'elfinder-resize-handle-hline',
  					vline   = 'elfinder-resize-handle-vline',
  					rpoint  = 'elfinder-resize-handle-point',
  					src     = fm.openUrl(file.hash),
  					dinit   = function() {
  						var dw,
  							winH  = $(window).height(),
  							winW  = $(window).width(),
  							ctrW  = dialog.find('div.elfinder-resize-control').width(),
  							prvW  = preview.width(),
  							baseW = base.width();
  						
  						base.width(Math.min(dialogWidth, winW - 30));
  						preview.attr('style', '');
  						if (owidth && oheight) {
  							pwidth  = preview.width()  - (rhandle.outerWidth()  - rhandle.width());
  							pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  							resize.updateView(owidth, oheight);
  						}
  						prvW  = preview.width(),
  						
  						dw = dialog.width() - 20;
  						if (prvW > dw) {
  							preview.width(dw);
  						} else if ((dw - prvW) < ctrW) {
  							if (winW > winH) {
  								preview.width(dw - ctrW - 20);
  							} else {
  								preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'});
  							}
  						}
  						pwidth  = preview.width()  - (rhandle.outerWidth()  - rhandle.width());
  						winH -= 30;
  						(preview.height() > winH) && preview.height(winH);
  						pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  						if (owidth && oheight) {
  							setupimg();
  						}
  						if (img.height() && preview.height() > img.height() + 20) {
  							preview.height(img.height() + 20);
  							pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  							setuprimg();
  						}
  					},
  					base;
  				
  				imgr.mousedown( rotate.start );
  				$(document).mouseup( rotate.stop );
  					
  				uiresize.append(
  					$(row).append($(label).text(fm.i18n('width')), width, reset),
  					$(row).append($(label).text(fm.i18n('height')), height),
  					$(row).append($('<label/>').text(fm.i18n('aspectRatio')).prepend(constr)),
  					(quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span/>').text(' (1-100)')) : $()),
  					$(row).append($(label).text(fm.i18n('scale')), uiprop)
  				);
  
  				if (api2) {
  					uicrop.append(
  						$(row).append($(label).text('X'), pointX),
  						$(row).append($(label).text('Y')).append(pointY),
  						$(row).append($(label).text(fm.i18n('width')), offsetX),
  						$(row).append($(label).text(fm.i18n('height')), offsetY),
  						(quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>').text(' (1-100)')) : $())
  					);
  					
  					uirotate.append(
  						$(row).append(
  							$(label).text(fm.i18n('rotate')),
  							degree,
  							$('<span/>').text(fm.i18n('degree')),
  							$(uibuttonset).append(uideg270, $(uiseparator), uideg90)
  						),
  						$(row).css('height', '20px').append(uidegslider),
  						(quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>').text(' (1-100)')) : $())
  					);
  				}
  				
  				dialog.append(uitype).on('resize', function(e){
  					e.stopPropagation();
  				});
  
  				if (api2) {
  					control.append($(row), uiresize, uicrop.hide(), uirotate.hide());
  				} else {
  					control.append($(row), uiresize);
  				}
  				control.find('input,select').attr('disabled', 'disabled');
  				
  				rhandle.append('<div class="'+hline+' '+hline+'-top"/>',
  					'<div class="'+hline+' '+hline+'-bottom"/>',
  					'<div class="'+vline+' '+vline+'-left"/>',
  					'<div class="'+vline+' '+vline+'-right"/>',
  					'<div class="'+rpoint+' '+rpoint+'-e"/>',
  					'<div class="'+rpoint+' '+rpoint+'-se"/>',
  					'<div class="'+rpoint+' '+rpoint+'-s"/>');
  					
  				preview.append(spinner).append(rhandle.hide()).append(img.hide());
  
  				if (api2) {
  					rhandlec.css('position', 'absolute')
  						.append('<div class="'+hline+' '+hline+'-top"/>',
  						'<div class="'+hline+' '+hline+'-bottom"/>',
  						'<div class="'+vline+' '+vline+'-left"/>',
  						'<div class="'+vline+' '+vline+'-right"/>',
  						'<div class="'+rpoint+' '+rpoint+'-n"/>',
  						'<div class="'+rpoint+' '+rpoint+'-e"/>',
  						'<div class="'+rpoint+' '+rpoint+'-s"/>',
  						'<div class="'+rpoint+' '+rpoint+'-w"/>',
  						'<div class="'+rpoint+' '+rpoint+'-ne"/>',
  						'<div class="'+rpoint+' '+rpoint+'-se"/>',
  						'<div class="'+rpoint+' '+rpoint+'-sw"/>',
  						'<div class="'+rpoint+' '+rpoint+'-nw"/>');
  
  					preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc)));
  					
  					preview.append(imgr.hide());
  				}
  				
  				preview.css('overflow', 'hidden');
  				
  				dialog.append(preview, control);
  				
  				buttons[fm.i18n('btnApply')] = save;
  				buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  				
  				base = fm.dialog(dialog, {
  					title          : fm.escape(file.name),
  					width          : dialogWidth,
  					resizable      : false,
  					buttons        : buttons,
  					open           : function() {
  						fm.bind('resize', dinit);
  						img.attr('src', src + (src.indexOf('?') === -1 ? '?' : '&')+'_='+Math.random());
  						imgc.attr('src', img.attr('src'));
  						imgr.attr('src', img.attr('src'));
  					},
  					close          : function() {
  						fm.unbind('resize', dinit);
  						$(this).hide().remove();
  					}
  				}).attr('id', id).parent();
  				
  				// for IE < 9 dialog mising at open second+ time.
  				if (fm.UA.ltIE8) {
  					$('.elfinder-dialog').css('filter', '');
  				}
  				
  				reset.css('left', width.position().left + width.width() + 12);
  				
  				coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}),
  				rhandlec.css('cursor', 'move');
  				rhandlec.find('.elfinder-resize-handle-point').css({
  					'background-color' : '#fff',
  					'opacity': 0.5,
  					'border-color':'#000'
  				});
  
  				imgr.css('cursor', 'pointer');
  
  				if (! api2) {
  					uitype.find('.api2').remove();
  				}
  				
  				uitype.controlgroup? uitype.controlgroup() : uitype.buttonset();
  
  			},
  			
  			id, dialog
  			;
  			
  
  		if (!files.length || files[0].mime.indexOf('image/') === -1) {
  			return dfrd.reject();
  		}
  		
  		id = 'resize-'+fm.namespace+'-'+files[0].hash;
  		dialog = fm.getUI().find('#'+id);
  		
  		if (dialog.length) {
  			dialog.elfinderdialog('toTop');
  			return dfrd.resolve();
  		}
  		
  		open(files[0], id);
  			
  		return dfrd;
  	};
  
  };
  
  (function ($) {
  	
  	var findProperty = function (styleObject, styleArgs) {
  		var i = 0 ;
  		for( i in styleArgs) {
  	        if (typeof styleObject[styleArgs[i]] != 'undefined') 
  	        	return styleArgs[i];
  		}
  		styleObject[styleArgs[i]] = '';
  	    return styleArgs[i];
  	};
  	
  	$.cssHooks.rotate = {
  		get: function(elem, computed, extra) {
  			return $(elem).rotate();
  		},
  		set: function(elem, value) {
  			$(elem).rotate(value);
  			return value;
  		}
  	};
  	$.cssHooks.transform = {
  		get: function(elem, computed, extra) {
  			var name = findProperty( elem.style , 
  				['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  			return elem.style[name];
  		},
  		set: function(elem, value) {
  			var name = findProperty( elem.style , 
  				['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  			elem.style[name] = value;
  			return value;
  		}
  	};
  	
  	$.fn.rotate = function(val) {
  		if (typeof val == 'undefined') {
  			if (!!window.opera) {
  				var r = this.css('transform').match(/rotate\((.*?)\)/);
  				return  ( r && r[1])?
  					Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0;
  			} else {
  				var r = this.css('transform').match(/rotate\((.*?)\)/);
  				return  ( r && r[1])? parseInt(r[1]) : 0;
  			}
  		}
  		this.css('transform', 
  			this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)');
  		return this;
  	};
  
  	$.fx.step.rotate  = function(fx) {
  		if ( fx.state == 0 ) {
  			fx.start = $(fx.elem).rotate();
  			fx.now = fx.start;
  		}
  		$(fx.elem).rotate(fx.now);
  	};
  
  	if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9
  		var GetAbsoluteXY = function(element) {
  			var pnode = element;
  			var x = pnode.offsetLeft;
  			var y = pnode.offsetTop;
  			
  			while ( pnode.offsetParent ) {
  				pnode = pnode.offsetParent;
  				if (pnode != document.body && pnode.currentStyle['position'] != 'static') {
  					break;
  				}
  				if (pnode != document.body && pnode != document.documentElement) {
  					x -= pnode.scrollLeft;
  					y -= pnode.scrollTop;
  				}
  				x += pnode.offsetLeft;
  				y += pnode.offsetTop;
  			}
  			
  			return { x: x, y: y };
  		};
  		
  		var StaticToAbsolute = function (element) {
  			if ( element.currentStyle['position'] != 'static') {
  				return ;
  			}
  
  			var xy = GetAbsoluteXY(element);
  			element.style.position = 'absolute' ;
  			element.style.left = xy.x + 'px';
  			element.style.top = xy.y + 'px';
  		};
  
  		var IETransform = function(element,transform){
  
  			var r;
  			var m11 = 1;
  			var m12 = 1;
  			var m21 = 1;
  			var m22 = 1;
  
  			if (typeof element.style['msTransform'] != 'undefined'){
  				return true;
  			}
  
  			StaticToAbsolute(element);
  
  			r = transform.match(/rotate\((.*?)\)/);
  			var rotate =  ( r && r[1])	?	parseInt(r[1])	:	0;
  
  			rotate = rotate % 360;
  			if (rotate < 0) rotate = 360 + rotate;
  
  			var radian= rotate * Math.PI / 180;
  			var cosX =Math.cos(radian);
  			var sinY =Math.sin(radian);
  
  			m11 *= cosX;
  			m12 *= -sinY;
  			m21 *= sinY;
  			m22 *= cosX;
  
  			element.style.filter =  (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) +
  				("progid:DXImageTransform.Microsoft.Matrix(" + 
  					 "M11=" + m11 + 
  					",M12=" + m12 + 
  					",M21=" + m21 + 
  					",M22=" + m22 + 
  					",FilterType='bilinear',sizingMethod='auto expand')") 
  				;
  
  	  		var ow = parseInt(element.style.width || element.width || 0 );
  	  		var oh = parseInt(element.style.height || element.height || 0 );
  
  			var radian = rotate * Math.PI / 180;
  			var absCosX =Math.abs(Math.cos(radian));
  			var absSinY =Math.abs(Math.sin(radian));
  
  			var dx = (ow - (ow * absCosX + oh * absSinY)) / 2;
  			var dy = (oh - (ow * absSinY + oh * absCosX)) / 2;
  
  			element.style.marginLeft = Math.floor(dx) + "px";
  			element.style.marginTop  = Math.floor(dy) + "px";
  
  			return(true);
  		};
  		
  		var transform_set = $.cssHooks.transform.set;
  		$.cssHooks.transform.set = function(elem, value) {
  			transform_set.apply(this, [elem, value] );
  			IETransform(elem,value);
  			return value;
  		};
  	}
  
  })(jQuery);
  
  
  /*
   * File: /js/commands/rm.js
   */
  
  /**
   * @class  elFinder command "rm"
   * Delete files
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.rm = function() {
  	
  	this.shortcuts = [{
  		pattern     : 'delete ctrl+backspace'
  	}];
  	
  	this.getstate = function(sel) {
  		var fm = this.fm;
  		sel = sel || fm.selected();
  		return !this._disabled && sel.length && $.map(sel, function(h) { var f = fm.file(h); return f && f.phash && !f.locked ? h : null }).length == sel.length
  			? 0 : -1;
  	}
  	
  	this.exec = function(hashes) {
  		var self   = this,
  			fm     = this.fm,
  			dfrd   = $.Deferred()
  				.fail(function(error) {
  					error && fm.error(error);
  				}),
  			files  = this.files(hashes),
  			cnt    = files.length,
  			cwd    = fm.cwd().hash,
  			tpl    = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>',
  			targets, text, f, fname, size, tmb, descs, dialog;
  
  		if (!cnt || this._disabled) {
  			return dfrd.reject();
  		}
  		
  		$.each(files, function(i, file) {
  			if (!file.phash) {
  				return !dfrd.reject(['errRm', file.name, 'errPerm']);
  			}
  			if (file.locked) {
  				return !dfrd.reject(['errLocked', file.name]);
  			}
  		});
  
  		if (dfrd.state() == 'pending') {
  			targets = this.hashes(hashes);
  			cnt     = files.length;
  			descs   = [];
  			
  			if (cnt > 1) {
  				if (!$.map(files, function(f) { return f.mime == 'directory' ? 1 : null ; }).length) {
  					size = 0;
  					$.each(files, function(h, f) { 
  						if (f.size && f.size != 'unknown') {
  							var s = parseInt(f.size);
  							if (s >= 0 && size >= 0) {
  								size += s;
  							}
  						} else {
  							size = 'unknown';
  							return false;
  						}
  					});
  					descs.push(fm.i18n('size')+': '+fm.formatSize(size));
  				}
  				text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))];
  			} else {
  				f = files[0];
  				tmb = fm.tmb(f);
  				if (f.size) {
  					descs.push(fm.i18n('size')+': '+fm.formatSize(f.size));
  				}
  				descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
  				fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
  				text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];
  				
  			}
  			
  			text.push('confirmRm');
  			
  			fm.lockfiles({files : targets});
  			dialog = fm.confirm({
  				title  : self.title,
  				text   : text,
  				accept : {
  					label    : 'btnRm',
  					callback : function() {  
  						fm.request({
  							data   : {cmd  : 'rm', targets : targets}, 
  							notify : {type : 'rm', cnt : cnt},
  							preventFail : true
  						})
  						.fail(function(error) {
  							dfrd.reject(error);
  						})
  						.done(function(data) {
  							dfrd.done(data);
  						})
  						.always(function() {
  							fm.unlockfiles({files : targets});
  						});
  					}
  				},
  				cancel : {
  					label    : 'btnCancel',
  					callback : function() {
  						fm.unlockfiles({files : targets});
  						if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) {
  							fm.select({selected : targets});
  						} else {
  							fm.selectfiles({files : targets});
  						}
  						dfrd.reject();
  					}
  				}
  			});
  			// load thumbnail
  			if (tmb) {
  				$('<img/>')
  					.on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  					.attr('src', tmb.url);
  			}
  		}
  			
  		return dfrd;
  	}
  
  };
  
  
  /*
   * File: /js/commands/search.js
   */
  
  /**
   * @class  elFinder command "search"
   * Find files
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.search = function() {
  	this.title          = 'Find files';
  	this.options        = {ui : 'searchbutton'}
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  	
  	/**
  	 * Return command status.
  	 * Search does not support old api.
  	 *
  	 * @return Number
  	 **/
  	this.getstate = function() {
  		return 0;
  	}
  	
  	/**
  	 * Send search request to backend.
  	 *
  	 * @param  String  search string
  	 * @return $.Deferred
  	 **/
  	this.exec = function(q, target, mime) {
  		var fm = this.fm,
  			reqDef;
  		
  		if (typeof q == 'string' && q) {
  			if (typeof target == 'object') {
  				mime = target.mime || '';
  				target = target.target || '';
  			}
  			target = target? target : '';
  			mime = mime? $.trim(mime).replace(',', ' ').split(' ') : [];
  			$.each(mime, function(){ return $.trim(this); });
  			fm.trigger('searchstart', {query : q, target : target, mimes : mime});
  			
  			reqDef = fm.request({
  				data   : {cmd : 'search', q : q, target : target, mimes : mime},
  				notify : {type : 'search', cnt : 1, hideCnt : true},
  				cancel : true
  			});
  			return reqDef;
  		}
  		fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').focus();
  		return $.Deferred().reject();
  	}
  
  };
  
  
  /*
   * File: /js/commands/sort.js
   */
  
  /**
   * @class  elFinder command "sort"
   * Change sort files rule
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.sort = function() {
  	var self  = this,
  		fm    = self.fm,
  		setVar = function() {
  			self.variants = [];
  			$.each(fm.sortRules, function(name, value) {
  				var sort = {
  						type  : name,
  						order : name == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  					};
  				if ($.inArray(name, fm.sorters) !== -1) {
  					var arr = name == fm.sortType ? (sort.order == 'asc'? 's' : 'n') : '';
  					self.variants.push([sort, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + '&nbsp;' + fm.i18n('sort'+name)]);			}
  			});
  			self.variants.push('|');
  			self.variants.push([
  				{
  					type  : fm.sortType,
  					order : fm.sortOrder,
  					stick : !fm.sortStickFolders
  				},
  				(fm.sortStickFolders? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortFoldersFirst')
  			]);
  		};
  	
  	/**
  	 * Command options
  	 *
  	 * @type  Object
  	 */
  	this.options = {ui : 'sortbutton'};
  	
  	fm.bind('open sortchange', setVar)
  	.bind('open', function() {
  		fm.unbind('add', setVar).one('add', setVar)
  		fm.getUI('toolbar').find('.elfiner-button-sort .elfinder-button-menu .elfinder-button-menu-item').each(function() {
  			var tgt = $(this),
  				rel = tgt.attr('rel');
  			tgt.toggle(! rel || $.inArray(rel, fm.sorters) !== -1);
  		});
  	})
  	.bind('cwdrender', function() {
  		var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table');
  		if (cols.length) {
  			$.each(fm.sortRules, function(name, value) {
  				var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name);
  				if (td.length) {
  					var current = ( name == fm.sortType),
  					sort = {
  						type  : name,
  						order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  					},arr;
  					if (current) {
  						td.addClass('ui-state-active');
  						arr = fm.sortOrder == 'asc' ? 'n' : 's';
  						$('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td);
  					}
  					$(td).on('click', function(e){
  						if (! $(this).data('dragging')) {
  							e.stopPropagation();
  							if (! fm.getUI('cwd').data('longtap')) {
  								self.exec([], sort);
  							}
  						}
  					})
  					.hover(function() {
  						$(this).addClass('ui-state-hover');
  					},function() {
  						$(this).removeClass('ui-state-hover');
  					});
  				}
  				
  			});
  		}
  	});
  	
  	this.getstate = function() {
  		return 0;
  	};
  	
  	this.exec = function(hashes, sortopt) {
  		var fm = this.fm,
  			sort = $.extend({
  				type  : fm.sortType,
  				order : fm.sortOrder,
  				stick : fm.sortStickFolders
  			}, sortopt);
  
  		return fm.lazy(function() {
  			fm.setSort(sort.type, sort.order, sort.stick);
  			this.resolve();
  		});
  	};
  
  };
  
  
  /*
   * File: /js/commands/up.js
   */
  
  /**
   * @class  elFinder command "up"
   * Go into parent directory
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.up = function() {
  	this.alwaysEnabled = true;
  	this.updateOnSelect = false;
  	
  	this.shortcuts = [{
  		pattern     : 'ctrl+up'
  	}];
  	
  	this.getstate = function() {
  		return this.fm.cwd().phash ? 0 : -1;
  	}
  	
  	this.exec = function() {
  		return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash) : $.Deferred().reject();
  	}
  
  };
  
  
  /*
   * File: /js/commands/upload.js
   */
  
  /**
   * @class elFinder command "upload"
   * Upload files using iframe or XMLHttpRequest & FormData.
   * Dialog allow to send files using drag and drop
   *
   * @type  elFinder.command
   * @author  Dmitry (dio) Levashov
   */
  elFinder.prototype.commands.upload = function() {
  	var hover = this.fm.res('class', 'hover');
  	
  	this.disableOnSearch = true;
  	this.updateOnSelect  = false;
  	
  	// Shortcut opens dialog
  	this.shortcuts = [{
  		pattern     : 'ctrl+u'
  	}];
  	
  	/**
  	 * Return command state
  	 *
  	 * @return Number
  	 **/
  	this.getstate = function(sel) {
  		var fm = this.fm, f,
  		sel = fm.directUploadTarget? [fm.directUploadTarget] : (sel || [fm.cwd().hash]);
  		if (!this._disabled && sel.length == 1) {
  			f = fm.file(sel[0]);
  		}
  		return (f && f.mime == 'directory' && f.write)? 0 : -1;
  	};
  	
  	
  	this.exec = function(data) {
  		var fm = this.fm,
  			targets = data && (data instanceof Array)? data : null,
  			check  = !targets && data && data.target? [ data.target ] : targets,
  			fmUpload = function(data) {
  				fm.upload(data)
  					.fail(function(error) {
  						dfrd.reject(error);
  					})
  					.done(function(data) {
  						var cwd = fm.getUI('cwd');
  						dfrd.resolve(data);
  						if (data && data.added && data.added[0]) {
  							var newItem = cwd.find('#'+fm.cwdHash2Id(data.added[0].hash));
  							if (newItem.length) {
  								newItem.trigger('scrolltoview');
  							}
  						}
  					});
  			},
  			upload = function(data) {
  				dialog.elfinderdialog('close');
  				if (targets) {
  					data.target = targets[0];
  				}
  				fmUpload(data);
  			},
  			dfrd = $.Deferred().always(function() {
  				//setTimeout(function() {
  				//	fm.autoSync();
  				//}, 1000);
  			}),
  			dialog, input, button, dropbox, pastebox, dropUpload, paste;
  		
  		//fm.autoSync('stop');
  		if (this.getstate(check) < 0) {
  			return dfrd.reject();
  		}
  		
  		dropUpload = function(e) {
  			e.stopPropagation();
  			e.preventDefault();
  			var file = false,
  				type = '',
  				elfFrom = null,
  				mycwd = '',
  				data = null,
  				target = e._target || null,
  				trf = e.dataTransfer || null,
  				kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '';
  			
  			if (trf) {
  				try {
  					elfFrom = trf.getData('elfinderfrom');
  					if (elfFrom) {
  						mycwd = window.location.href + fm.cwd().hash;
  						if ((!target && elfFrom === mycwd) || target === mycwd) {
  							dfrd.reject();
  							return;
  						}
  					}
  				} catch(e) {}
  				
  				if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) {
  					file = trf;
  					type = 'data';
  				} else if (kind !== 'string' && trf.files && trf.files.length && $.inArray('Text', trf.types) === -1) {
  					file = trf.files;
  					type = 'files';
  				} else {
  					try {
  						if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) {
  							file = [ data ];
  							type = 'html';
  						}
  					} catch(e) {}
  					if (! file && (data = trf.getData('text'))) {
  						file = [ data ];
  						type = 'text';
  					}
  				}
  			}
  			if (file) {
  				fmUpload({files : file, type : type, target : target});
  			} else {
  				dfrd.reject();
  			}
  		};
  		
  		if (!targets && data) {
  			if (data.input || data.files) {
  				data.type = 'files';
  				fmUpload(data);
  			} else if (data.dropEvt) {
  				dropUpload(data.dropEvt);
  			}
  			return dfrd;
  		}
  		
  		paste = function(e) {
  			var e = e.originalEvent || e;
  			var files = [], items = [];
  			var file;
  			if (e.clipboardData) {
  				if (e.clipboardData.items && e.clipboardData.items.length){
  					items = e.clipboardData.items;
  					for (var i=0; i < items.length; i++) {
  						if (e.clipboardData.items[i].kind == 'file') {
  							file = e.clipboardData.items[i].getAsFile();
  							files.push(file);
  						}
  					}
  				} else if (e.clipboardData.files && e.clipboardData.files.length) {
  					files = e.clipboardData.files;
  				}
  				if (files.length) {
  					upload({files : files, type : 'files'});
  					return;
  				}
  			}
  			var my = e.target || e.srcElement;
  			setTimeout(function () {
  				if (my.innerHTML) {
  					$(my).find('img').each(function(i, v){
  						if (v.src.match(/^webkit-fake-url:\/\//)) {
  							// For Safari's bug.
  							// ref. https://bugs.webkit.org/show_bug.cgi?id=49141
  							//      https://dev.ckeditor.com/ticket/13029
  							$(v).remove();
  						}
  					});
  					var src = my.innerHTML.replace(/<br[^>]*>/gi, ' ');
  					var type = src.match(/<[^>]+>/)? 'html' : 'text';
  					my.innerHTML = '';
  					upload({files : [ src ], type : type});
  				}
  			}, 1);
  		};
  		
  		input = $('<input type="file" multiple="true"/>')
  			.change(function() {
  				upload({input : input[0], type : 'files'});
  			})
  			.on('dragover', function(e) {
  				e.originalEvent.dataTransfer.dropEffect = 'copy';
  			});
  
  		button = $('<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+fm.i18n('selectForUpload')+'</span></div>')
  			.append($('<form/>').append(input))
  			.hover(function() {
  				button.toggleClass(hover)
  			});
  			
  		dialog = $('<div class="elfinder-upload-dialog-wrapper"/>')
  			.append(button);
  		
  		pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>')
  			.on('paste drop', function(e){
  				paste(e);
  			})
  			.on('mousedown click', function(){
  				$(this).focus();
  			})
  			.on('focus', function(){
  				this.innerHTML = '';
  			})
  			.on('dragenter mouseover', function(){
  				pastebox.addClass(hover);
  			})
  			.on('dragleave mouseout', function(){
  				pastebox.removeClass(hover);
  			});
  		
  		if (fm.dragUpload) {
  			dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropPasteFiles')+'</div>')
  				.on('paste', function(e){
  					paste(e);
  				})
  				.on('mousedown click', function(){
  					$(this).focus();
  				})
  				.on('focus', function(){
  					this.innerHTML = '';
  				})
  				.on('mouseover', function(){
  					$(this).addClass(hover);
  				})
  				.on('mouseout', function(){
  					$(this).removeClass(hover);
  				})
  				.prependTo(dialog)
  				.after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  			
  			dropbox.addEventListener('dragenter', function(e) {
  				e.stopPropagation();
  			  	e.preventDefault();
  				$(dropbox).addClass(hover);
  			}, false);
  
  			dropbox.addEventListener('dragleave', function(e) {
  				e.stopPropagation();
  			  	e.preventDefault();
  				$(dropbox).removeClass(hover);
  			}, false);
  
  			dropbox.addEventListener('dragover', function(e) {
  				e.stopPropagation();
  			  	e.preventDefault();
  				e.dataTransfer.dropEffect = 'copy';
  			  	$(dropbox).addClass(hover);
  			}, false);
  
  			dropbox.addEventListener('drop', function(e) {
  				dialog.elfinderdialog('close');
  				targets && (e._target = targets[0]);
  				dropUpload(e);
  			}, false);
  			
  		} else {
  			pastebox
  				.prependTo(dialog)
  				.after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  			
  		}
  		
  		fm.dialog(dialog, {
  			title          : this.title + (targets? ' - ' + fm.escape(fm.file(targets[0]).name) : ''),
  			modal          : true,
  			resizable      : false,
  			destroyOnClose : true
  		});
  		
  		return dfrd;
  	};
  
  };
  
  
  /*
   * File: /js/commands/view.js
   */
  
  /**
   * @class  elFinder command "view"
   * Change current directory view (icons/list)
   *
   * @author Dmitry (dio) Levashov
   **/
  elFinder.prototype.commands.view = function() {
  	var fm = this.fm;
  	this.value          = fm.viewType;
  	this.alwaysEnabled  = true;
  	this.updateOnSelect = false;
  
  	this.options = { ui : 'viewbutton'};
  	
  	this.getstate = function() {
  		return 0;
  	}
  	
  	this.exec = function() {
  		var self  = this,
  			value = fm.storage('view', this.value == 'list' ? 'icons' : 'list');
  		return fm.lazy(function() {
  			fm.viewchange();
  			self.update(void(0), value);
  			this.resolve();
  		});
  	}
  
  };
  
  })(jQuery);