: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @class elFinder command "help"
* @author Dmitry (dio) Levashov
(elFinder.prototype.commands.help = function() {
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>',
prim = 'ui-priority-primary',
sec = 'ui-priority-secondary',
lic = 'elfinder-help-license',
tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}" class="ui-tabs-anchor">{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-top">'],
stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
sep = '<div class="elfinder-help-separator"></div>',
selfUrl = jQuery('base').length? fm.escape(document.location.href.replace(/#.*$/, '')) : '',
clTabActive = fm.res('class', 'tabsactive'),
if (fm.theme && fm.theme.author) {
src = atpl[r]('elfinder-help-team', 'elfinder-help-team elfinder-help-term-theme')[r](author, fm.i18n(fm.theme.author) + (fm.theme.email? ' <'+fm.theme.email+'>' : ''))[r](work, fm.i18n('theme') + ' ('+fm.i18n(fm.theme.name)+')');
src = '<div class="elfinder-help-team elfinder-help-term-theme" style="display:none"></div>';
html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"></div>');
html.push('<h3>elFinder</h3>');
html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+'</div>');
html.push('<div class="'+sec+'">'+fm.i18n('protocolver')+': <span class="apiver"></span></div>');
html.push('<div class="'+sec+'">jQuery/jQuery UI: '+jQuery().jquery+'/'+jQuery.ui.version+'</div>');
html.push(linktpltgt[r](url, 'https://studio-42.github.io/elFinder/')[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('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
html.push(atpl[r](author, 'Dmitry "dio" Levashov <dio@std42.ru>')[r](work, fm.i18n('chiefdev')));
html.push(atpl[r](author, 'Naoki Sawada <hypweb+elfinder@gmail.com>')[r](work, fm.i18n('developer')));
html.push(atpl[r](author, 'Troex Nevelin <troex@fury.scancode.ru>')[r](work, fm.i18n('maintainer')));
html.push(atpl[r](author, 'Alexey Sukhotin <strogg@yandex.ru>')[r](work, fm.i18n('contributor')));
if (fm.i18[fm.lang].translator) {
jQuery.each(fm.i18[fm.lang].translator.split(', '), function() {
html.push(atpl[r](author, jQuery.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a>, <a href="https://icons8.com" target="_blank">Icons8</a></div>');
html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>');
html.push('<div class="'+lic+'">Copyright © 2009-2021, Studio 42</div>');
html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
jQuery.each(sh, function(i, s) {
html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
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>');
integrations = function() {
html.push('<div id="'+fm.namespace+'-help-integrations" class="ui-tabs-panel ui-widget-content ui-corner-bottom"></div>');
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"><ul></ul></div>');
debugRender = function() {
var render = function(elm, obj) {
jQuery.each(obj, function(k, v) {
elm.append(jQuery('<dt></dt>').text(k));
if (typeof v === 'undefined') {
elm.append(jQuery('<dd></dd>').append(jQuery('<span></span>').text('undfined')));
} else if (typeof v === 'object' && !v) {
elm.append(jQuery('<dd></dd>').append(jQuery('<span></span>').text('null')));
} else if (typeof v === 'object' && (jQuery.isPlainObject(v) || v.length)) {
elm.append( jQuery('<dd></dd>').append(render(jQuery('<dl></dl>'), v)));
elm.append(jQuery('<dd></dd>').append(jQuery('<span></span>').text((v && typeof v === 'object')? '[]' : (v? v : '""'))));
cnt = debugUL.children('li').length,
if (self.debug.options || self.debug.debug) {
lastUL = debugUL.children('li:last');
lastDIV = debugDIV.children('div:last');
if (lastDIV.is(':hidden')) {
tabId = fm.namespace + '-help-debug-' + (+new Date());
targetL = jQuery('<li></li>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL);
target = jQuery('<div id="'+tabId+'"></div>').data('debug', self.debug);
targetL.on('click.debugrender', function() {
var debug = target.data('debug');
target.removeData('debug');
info = jQuery('<fieldset>').append(jQuery('<legend></legend>').text('debug'), render(jQuery('<dl></dl>'), debug.debug));
info = jQuery('<fieldset>').append(jQuery('<legend></legend>').text('options'), render(jQuery('<dl></dl>'), debug.options));
targetL.off('click.debugrender');
opened && debugDIV.tabs('refresh');
opened, tabInteg, integDIV, tabDebug, debugDIV, debugUL;
this.alwaysEnabled = true;
this.updateOnSelect = false;
fm.bind('load', function() {
var parts = self.options.view || ['about', 'shortcuts', 'help', 'integrations', 'debug'],
i, helpSource, tabBase, tabNav, tabs, delta;
// remove 'preference' tab, it moved to command 'preference'
if ((i = jQuery.inArray('preference', parts)) !== -1) {
// debug tab require jQueryUI Tabs Widget
if ((i = jQuery.inArray(parts, 'debug')) !== -1) {
jQuery.each(parts, function(i, title) {
html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
jQuery.inArray('about', parts) !== -1 && about();
jQuery.inArray('shortcuts', parts) !== -1 && shortcuts();
if (jQuery.inArray('help', parts) !== -1) {
helpSource = fm.i18nBaseUrl + 'help/%s.html.js';
jQuery.inArray('integrations', parts) !== -1 && integrations();
jQuery.inArray('debug', parts) !== -1 && debug();
content = jQuery(html.join(''));
content.find('.ui-tabs-nav li')
.on('mouseenter mouseleave', function(e) {
jQuery(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
.on('focus blur', 'a', function(e) {
jQuery(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin');
.on('click', function(e) {
link.parent().addClass(clTabActive).siblings().removeClass(clTabActive);
content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show();
.filter(':first').trigger('click');
tabInteg = content.find('.elfinder-help-tab-integrations').hide();
integDIV = content.find('#'+fm.namespace+'-help-integrations').hide().append(jQuery('<div class="elfinder-help-integrations-desc"></div>').html(fm.i18n('integrationWith')));
fm.bind('helpIntegration', function(e) {
var ul = integDIV.children('ul:first'),
data, elm, cmdUL, cmdCls;
if (jQuery.isPlainObject(e.data)) {
if (data.title || data.link) {
elm = jQuery('<a></a>').attr('href', data.link).attr('target', '_blank').text(data.title);
elm = jQuery('<span></span>').text(data.title);
elm = jQuery('<span></span>').append(jQuery('<img/>').attr(data.banner), elm);
elm.filter('a').each(function() {
if (!tgt.attr('target')) {
tgt.attr('target', '_blank');;
ul = jQuery('<ul class="elfinder-help-integrations"></ul>').appendTo(integDIV);
cmdCls = 'elfinder-help-integration-' + data.cmd;
cmdUL = ul.find('ul.' + cmdCls);
cmdUL = jQuery('<ul class="'+cmdCls+'"></ul>');
ul.append(jQuery('<li></li>').append(jQuery('<span></span>').html(fm.i18n('cmd'+data.cmd))).append(cmdUL));
elm = cmdUL.append(jQuery('<li></li>').append(elm));
ul.append(jQuery('<li></li>').append(elm));
}).bind('themechange', function() {
content.find('div.elfinder-help-term-theme').replaceWith(getTheme());
tabDebug = content.find('.elfinder-help-tab-debug').hide();
debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first');
debugUL = debugDIV.children('ul:first').on('click', function(e) {
fm.bind('backenddebug', function(e) {
// CAUTION: DO NOT TOUCH `e.data`
if (useDebug && e.data && e.data.debug) {
self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) };
content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api);
self.dialog = self.fmDialog(content, {
debugDIV.tabs('destroy');
.on('click', function(e) {
tabBase = self.dialog.children('.ui-tabs');
tabNav = tabBase.children('.ui-tabs-nav:first');
tabs = tabBase.children('.ui-tabs-panel');
delta = self.dialog.outerHeight(true) - self.dialog.height();
self.dialog.closest('.ui-dialog').on('resize', function() {
tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20);
self.dialog.one('initContents', function() {
url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang),
}).done(function(source) {
jQuery('#'+fm.namespace+'-help-help').html(source);
url: helpSource.replace('%s', 'en'),
}).done(function(source) {
jQuery('#'+fm.namespace+'-help-help').html(source);
fm.trigger('helpBuilded', self.dialog);
}).one('open', function() {
fm.one('backenddebug', function() {
}).one('opendone', function() {
requestAnimationFrame(function() {
if (! debug && useDebug) {
this.getstate = function() {
this.exec = function(sel, opts) {
var tab = opts? opts.tab : void(0),
debugUL.find('a:first').trigger('click');
this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click');
return jQuery.Deferred().resolve();
}).prototype = { forceLoad : true }; // this is required command