: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* elFinder - file manager for web
* Version 2.1.49 (2019-04-14)
* Copyright 2009-2019, Studio 42
* Licensed under a 3-clauses BSD license
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery','jquery-ui'], factory);
} else if (typeof exports !== 'undefined') {
ui = require('jquery-ui');
module.exports = factory($, ui);
// Browser globals (Note: root is window)
factory(root.jQuery, root.jQuery.ui, true);
}(this, function($, _ui, toGlobal) {
toGlobal = toGlobal || false;
* @class elFinder - file manager for web
* @author Dmitry (dio) Levashov
var elFinder = function(elm, opts, bootCallback) {
* Objects array of jQuery.Deferred that calls before elFinder boot up
* Plugin name to check for conflicts with bootstrap etc
conflictChecks = ['button', 'tooltip'],
* Node on which elfinder creating
* Object of events originally registered in this node
prevEvents = jQuery.extend(true, {}, jQuery._data(node.get(0), 'events')),
prevContent = jQuery('<div/>').append(node.contents()).attr('class', node.attr('class') || '').attr('style', node.attr('style') || ''),
* Instance ID. Required to get/set cookie
id = node.attr('id') || '',
namespace = 'elfinder-' + (id ? id : Math.random().toString().substr(2, 7)),
mousedown = 'mousedown.'+namespace,
keydown = 'keydown.'+namespace,
keypress = 'keypress.'+namespace,
keyup = 'keyup.'+namespace,
* Is shortcuts/commands enabled
* Store enabled value before ajax request
* List of build-in events which mapped into methods with same names
events = ['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'],
* Rules to validate data from backend
* Current working directory hash
* Current working directory options default
* Current working directory options
* Hidden Files/dirs cache
* Files/dirs hash cache of each dirs
* Buffer for copied files
* Copied/cuted files hashes
* Prevent from remove its from cache.
* Required for dispaly correct files names in error messages
* Queue for 'open' requests
* Queue for only cwd requests e.g. `tmb`
base = new self.command(self),
* Number: pixcel or String: Number + "%"
* Base node object or selector
* Element which is the reference of the height percentage
* @default null | jQuery(window) (if height is percentage)
* MIME type list(Associative array) handled as a text file
* elfinder path for sound played on remove
* JSON.stringify of previous fm.sorters
* Map table of file extention to MIME-Type
beeper = jQuery(document.createElement('audio')).hide().appendTo('body')[0],
// NOTES: Do not touch data object
var volumeid, contextmenu, emptyDirs = {}, stayDirs = {},
rmClass, hashes, calc, gc, collapsed, prevcwd, sorterStr;
// support volume driver option `uiCmdMap`
self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
uiCmdMapPrev = JSON.stringify(self.commandMap);
// remove only files from prev cwd
// and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
collapsed = self.res('class', 'navcollapse');
hashes = Object.keys(files);
var isDir = (files[i].mime === 'directory'),
&& self.navHash2Elm(files[i].hash).is(':hidden')
&& self.navHash2Elm(phash).next('.elfinder-navbar-subtree').children().length > 100
&& (isDir || phash !== cwd)
if (isDir && !emptyDirs[phash]) {
.next('.elfinder-navbar-subtree').empty();
gcJobRes && gcJobRes._abort();
gcJobRes = self.asyncJob(calc, hashes, {
var hd = self.storage('hide') || {items: {}};
if (Object.keys(hiddenFiles).length) {
jQuery.each(hiddenFiles, function(h) {
self.trigger('filesgc').one('filesgc', function() {
self.one('opendone', function() {
if (! node.data('lazycnt')) {
self.one('lazydone', gc);
// trigger event 'sorterupdate'
sorterStr = JSON.stringify(self.sorters);
if (prevSorterStr !== sorterStr) {
self.trigger('sorterupdate');
prevSorterStr = sorterStr;
* Store info about files/dirs in "files" object.
* @param String data type
cache = function(data, type) {
var defsorter = { name: true, perm: true, date: true, size: true, kind: true },
sorterChk = !self.sorters._checked,
setSorter = function(file) {
jQuery.each(self.sortRules, function(key) {
if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
self.sorters = self.arrayFlip(sorters, true);
self.sorters._checked = true;
hideData = self.storage('hide') || {},
hides = hideData.items || {},
f, i, keepProp, parents, hidden;
for (i = 0; i < l; i++) {
f = Object.assign({}, data[i]);
hidden = (!hideData.show && hides[f.hash])? true : false;
if (f.name && f.hash && f.mime) {
if (sorterChk && f.phash === cwd) {
if (f.phash && (type === 'add' || type === 'change')) {
if (parents = self.parents(f.phash)) {
jQuery.each(parents, function() {
changedParents[this] = true;
jQuery.each(keeps, function() {
if(files[f.hash][this] && ! f[this]) {
f[this] = files[f.hash][this];
if (f.sizeInfo && !f.size) {
f.size = f.sizeInfo.size;
deleteCache(files[f.hash], true);
if (f.mime === 'directory' && !ownFiles[f.hash]) {