* @class elFinder command "rename".
* @author Dmitry (dio) Levashov, dio@std42.ru
elFinder.prototype.commands.rename = function() {
// set alwaysEnabled to allow root rename on client size
this.alwaysEnabled = true;
this.syncTitleOnChange = true;
request = function(dfrd, targtes, file, name) {
var sel = targtes? [file.hash].concat(targtes) : [file.hash],
fm.lockfiles({files : sel});
if (fm.isRoot(file) && !file.netkey) {
if (!(rootNames = fm.storage('rootNames'))) {
if (rootNames[file.hash]) {
delete rootNames[file.hash];
fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
if (typeof file._name === 'undefined') {
file.name = rootNames[file.hash] = name;
fm.storage('rootNames', rootNames);
data = { changed: [file] };
dfrd && dfrd.resolve(data);
fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
data['targets'] = targtes;
notify : {type : 'rename', cnt : cnt},
var err = fm.parseError(error);
if (! err || ! Array.isArray(err) || err[0] !== 'errRename') {
if (data.added && data.added.length && cnt === 1) {
data : {cmd : 'rename', target : data.added[0].hash, name : file.name},
notify : {type : 'undo', cnt : 1}
data : {cmd : 'rename', target : file.hash, name : name},
notify : {type : 'rename', cnt : 1}
dfrd && dfrd.resolve(data);
if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) {
fm.exec('open', $.map(data.added, function(f) {
return (f.mime === 'directory')? f.hash : null;
fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
getHint = function(name, target) {
var sel = target || fm.selected(),
splits = fm.splitFileExtention(name),
ext = splits[1]? ('.' + splits[1]) : '';
if (splits[1] && splits[0] === '*') {
hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", ';
hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"';
} else if (splits[0].length > 1) {
if (splits[0].substr(-1) === '*') {
add = splits[0].substr(0, splits[0].length - 1);
hint = '"' + add + f1.name+'", ';
hint += '"' + add + f2.name+'"';
} else if (splits[0].substr(0, 1) === '*') {
add = splits[0].substr(1);
hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", ';
hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"';
hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"';
batchRename = function() {
tplr = '<input name="type" type="radio" class="elfinder-tabstop">',
mkChk = function(node, label) {
return $('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node);
name = $('<input type="text" class="ui-corner-all elfinder-tabstop">'),
checks = $('<div></div>').append(
mkChk(num, 'plusNumber'),
mkChk(prefix, 'asPrefix'),
mkChk(suffix, 'asSuffix'),
mkChk(extention, 'changeExtention')
preview = $('<div class="elfinder-rename-batch-preview"></div>'),
node = $('<div class="elfinder-rename-batch"></div>').append(
$('<div class="elfinder-rename-batch-name"></div>').append(name),
$('<div class="elfinder-rename-batch-type"></div>').append(checks),
title : fm.i18n('batchRename'),
width: Math.min(380, fm.getUI().width() - 20),
name.on('input', mkPrev).trigger('focus');
ext = fm.splitFileExtention(fm.file(sel[0]).name)[1];
if (vName !== '' || num.is(':checked')) {
if (prefix.is(':checked')) {
} else if (suffix.is(':checked')) {
vName = '*' + vName + '.' + ext;
} else if (extention.is(':checked')) {
preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)]));
radios = checks.find('input:radio').on('change', mkPrev),
opts.buttons[fm.i18n('btnApply')] = function() {
dialog.elfinderdialog('close');
file = fm.file(targets.shift());
request(void(0), targets, file, vName);
opts.buttons[fm.i18n('btnCancel')] = function() {
dialog.elfinderdialog('close');
if ($.fn.checkboxradio) {
create: function(e, ui) {
if (this === num.get(0)) {
num.prop('checked', true).change();
create: function(e, ui) {
num.prop('checked', true).change();
dialog = self.fmDialog(node, opts);
this.noChangeDirOnRemovedCwd = true;
pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '')
description : 'batchRename',
fm.selected().length > 1 && batchRename();
this.getstate = function(select) {
var sel = this.files(select),
phash, ext, mime, brk, state, isRoot;
if (cnt > 1 && sel[0].phash) {
ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase();
isRoot = fm.isRoot(sel[0]);
state = (cnt === 1 && ((fm.cookieEnabled && isRoot) || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.grep(sel, function(f) {
if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) {
// because alwaysEnabled = true, it need check disabled on connector
if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) {
if (state !== -1 && cnt > 1) {
.attr({title: fm.i18n('batchRename')})
.on('click touchstart', function(e){
if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
fm.getUI().trigger('click'); // to close the context menu immediately
this.exec = function(hashes, cOpts) {
var cwd = fm.getUI('cwd'),
sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
file = fm.file(sel.shift()),
filename = '.elfinder-cwd-filename',
incwd = (fm.cwd().hash == file.hash),
type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'),
navbar = (type !== 'files'),
target = fm[navbar? 'navHash2Elm' : 'cwdHash2Elm'](file.hash),
tarea = (!navbar && fm.storage('view') != 'list'),
var ext = fm.splitFileExtention(name)[1];
return [name.substr(0, name.length - ext.length - 1), ext];
requestAnimationFrame(function() {
input && input.trigger('blur');
if (!overlay.is(':hidden')) {
overlay.elfinderoverlay('hide').off('click close', cancel);
pnode.removeClass('ui-front')
.off('unselect.'+fm.namespace, unselect);
node && node.css('max-height', '');
.parent('td').css('overflow', '');
var parent = input.parent(),
name = fm.escape(file.i18 || file.name);
name = name.replace(/([_.])/g, '​$1');
requestAnimationFrame(function() {
target.find(filename).html(name);
error && fm.error(error);
fm.unbind('resize', resize);
var name = $.trim(input.val()),
splits = fm.splitFileExtention(name),
input.replaceWith(fm.escape(name));
node.html(fm.escape(name));
request(dfrd, sel, file, name);
if (!overlay.is(':hidden')) {
pnode.css('z-index', '');
input.replaceWith(fm.escape(file.name));
node.html(fm.escape(file.name));
if (!inError && pnode.length) {
if (cnt === 1 && name === file.name) {
if (fm.options.validName && fm.options.validName.test) {
valid = fm.options.validName.test(name);
if (name === '.' || name === '..' || !valid) {
fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
if (cnt === 1 && fm.fileByName(name, file.phash)) {
fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))],
fm.trigger('unselectfiles', {files: fm.selected()})
.trigger('selectfiles', {files : [file.hash].concat(sel)});
input = $(tarea? '<textarea></textarea>' : '<input type="text"/>')
.on('keyup text', function(){
this.style.height = '1px';
this.style.height = this.scrollHeight + 'px';
this.style.width = colwidth + 'px';
if (this.scrollWidth > colwidth) {
this.style.width = this.scrollWidth + 10 + 'px';
.on('keydown', function(e) {
e.stopImmediatePropagation();
if (e.keyCode == $.ui.keyCode.ESCAPE) {
} else if (e.keyCode == $.ui.keyCode.ENTER) {
.on('mousedown click dblclick', function(e) {
if (e.type === 'dblclick') {
.on('dragenter dragleave dragover drop', function(e) {
// stop bubbling to prevent upload with native drop event
var name = fm.splitFileExtention(input.val())[0];
if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
overlay.on('click close', cancel).elfinderoverlay('show');
pnode.css('z-index', overlay.css('z-index') + 1);
! fm.enabled() && fm.enable();
input.trigger('focus').trigger('select');
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); })
overlay = fm.getUI('overlay'),