: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* elFinder current working directory ui.
* @author Dmitry (dio) Levashov
jQuery.fn.elfindercwd = function(fm, options) {
this.not('.elfinder-cwd').each(function() {
var mobile = fm.UA.Mobile,
list = fm.viewType == 'list',
evtSelect = 'select.'+fm.namespace,
* Unselect event full name
evtUnselect = 'unselect.'+fm.namespace,
* Disable event full name
evtDisable = 'disable.'+fm.namespace,
* Disable event full name
evtEnable = 'enable.'+fm.namespace,
clFile = fm.res(c, 'cwdfile'),
fileSelector = '.'+clFile,
clSelected = 'ui-selected',
clDisabled = fm.res(c, 'disabled'),
clDraggable = fm.res(c, 'draggable'),
clDroppable = fm.res(c, 'droppable'),
clHover = fm.res(c, 'hover'),
clActive = fm.res(c, 'active'),
clDropActive = fm.res(c, 'adroppable'),
* Css class for temporary nodes (for mkdir/mkfile) commands
* Select checkbox css class
clSelChk = 'elfinder-cwd-selectchk',
* Number of thumbnails to load in one request (new api only)
tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
* Currect clipboard(cut) hashes as object key
* incsearch current hashes
* Custom columns name and order
* Current clicked element id of first time for dblclick
customColsBuild = function() {
for (var i = 0; i < customCols.length; i++) {
cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
* Make template.row from customCols
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}></span>{marker}<span class="elfinder-cwd-filename">{name}</span></div>'+selectCheckbox+'</td>'+customColsBuild()+'</tr>';
selectCheckbox = (jQuery.map(options.showSelectCheckboxUA, function(t) {return (fm.UA[t] || t.match(/^all$/i))? true : null;}).length)? '<div class="elfinder-cwd-select"><input type="checkbox" class="'+clSelChk+'"></div>' : '',
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}></div>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div>'+selectCheckbox+'</div>',
permsTpl = fm.res('tpl', 'perms'),
lockTpl = fm.res('tpl', 'lock'),
symlinkTpl = fm.res('tpl', 'symlink'),
* Template placeholders replacement rules
return fm.cwdHash2Id(f.hash);
var name = fm.escape(f.i18 || f.name);
!list && (name = name.replace(/([_.])/g, '​$1'));
nametitle : function(f) {
return fm.escape(f.i18 || f.name);
permsclass : function(f) {
return fm.perms2class(f);
return fm.formatPermissions(f);
var cName = f.mime == 'directory' ? 'directory' : '';
f.isroot && (cName += ' isroot');
f.csscls && (cName += ' ' + fm.escape(f.csscls));
options.getClass && (cName += ' ' + options.getClass(f));
return f.icon? fm.getIconStyle(f) : '';
var cName = fm.mime2class(f.mime);
f.icon && (cName += ' elfinder-cwd-bgurl');
return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
return f.perm? fm.formatFileMode(f.perm) : '';
return f.perm? fm.formatFileMode(f.perm, 'string') : '';
return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
return f.perm? fm.formatFileMode(f.perm, 'both') : '';
return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, ' ') : '';
info += (info? ' ' : '') + fm.escape(f.i18 || f.name);
return info? info + ' ' + title : title;
* Type badge CSS added flag
* Type badge style sheet element
* Add type badge CSS into 'head'
addBadgeStyle = function(mime, name) {
if (mime && ! addedBadges[mime]) {
if (typeof addBadgeStyleSheet === 'undefined') {
if (jQuery('#elfinderAddBadgeStyle'+fm.namespace).length) {
jQuery('#elfinderAddBadgeStyle'+fm.namespace).remove();
addBadgeStyleSheet = jQuery('<style id="addBadgeStyle'+fm.namespace+'"></style>').insertBefore(jQuery('head').children(':first')).get(0).sheet || null;
if (addBadgeStyleSheet) {
mime = mime.toLowerCase();
ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]);
sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-');
if (typeof type[1] !== 'undefined') {
sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-');
addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0);
addedBadges[mime] = true;
* @param Object file info
f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name);
return templates[list ? 'row' : 'icon']
.replace(/\{([a-z0-9_]+)\}/g, function(s, e) {
return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : '');
* jQueery node that will be selected next
* @type Object jQuery node
* Flag. Required for msie to avoid unselect files on dragstart
* Move selection to prev/next file
* @param String move direction
* @param Boolean append to current selection
select = function(keyCode, append) {
var code = jQuery.ui.keyCode,
prev = keyCode == code.LEFT || keyCode == code.UP,
sel = cwd.find('[id].'+clSelected),
selector = prev ? 'first:' : 'last',
function sibling(n, direction) {
return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
s = sel.filter(prev ? ':first' : ':last');
sib = sibling(s, prev ? 'prev' : 'next');
// there is no sibling on required side - do not move selection
} else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
// find real prevoius file
// find up/down side file in icons view
left = s.position().left;
} while (n.length && !(n.position().top < top && n.position().left <= left));
if (n.hasClass(clDisabled)) {
} while (n.length && !(n.position().top > top && n.position().left >= left));
if (n.hasClass(clDisabled)) {
// there is row before last one - select last file
sib = cwd.find('[id]:not(.'+clDisabled+'):last');
if (sib.position().top > top) {
// !append && unselectAll();
if (selectedNext.length) {
n = prev? selectedNext.prev() : selectedNext;
// 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')) {
// append new files to selected
n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
// unselect selected files
sel.trigger(evtUnselect);
scrollToView(n.filter(prev ? ':first' : ':last'));
selectFile = function(hash) {
fm.cwdHash2Elm(hash).trigger(evtSelect);
var phash = fm.cwd().hash;
selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) {
unselectAll({ notrigger: true });
files = jQuery.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
files = files.slice(0, fm.maxTargets);
jQuery.each(files, function(i, v) {
selectedFiles[v.hash] = true;
fm.cwdHash2Elm(v.hash).trigger(evtSelect);
fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])});
cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
selectCheckbox && selectAllCheckbox.data('pending', false);