//Includes generic/enhanced variants of: //- RenameFromClipboard (http://resource.dopus.com/viewtopic.php?f=35&t=22867) //- Numbering of items supporting ".. of {total}" and others (http://resource.dopus.com/viewtopic.php?f=3&t=24534) // change log // ############################################################################ // v0.3.3 - 2025-12-26 // - set default log level to normal, fix SAFEMODE log output // v0.3.2 - 2025-12-25 // - fix script error, because "COMMANDS" option was missing after merging // - turns out, even more merging errors were present, sorry for that. // v0.3.1 - 2025-09-09 // - use FSMagic v0.61 // - reorder change log // - import fix from RenameFromClipboard v0.3.1: fix FSMagic.getBasename() returning "" for files without extension // - import fix from RenameFromClipboard v0.3.1: add placeholder %item.new_name_stem% // v0.3 - 2015-10 // - new option "RECURSE", recursive rename for all items (native rename does not support this for folders) // v0.2 - 2015-05 // - new option "FROMCLIP", renders RenameFromClipboard command obsolete // - passing of pad-with changed to PAD="width=0" // - passing of pad-size changed to PAD="size=2" // v0.1.1 - 2015-05 // - unwanted output fixed // - padding error fixed // v0.1 - 2015-05 // - initial version // todos: // - something is odd when using files (not text) from clipboard as new filenames when renaming // todos taken from RenameFromClipboard (v0.3.1): // not sure what the status of these todos is, maybe they are "dids" already // - autodetecting for existence of fileextensions in the clip content // to not accidentally remove extensions on renamed files if clip content // contains only pure filenames and no extensions // - KEEPEXTension mode // ############################################################################ var template = //main features.. "NUMBER/O,"+ "FROM/K,TO/K,"+ "REMOVENUM/S,BY/K[<1>],PAD/O[size,with],"+ "PATTERN/K,"+ "NUMBERVARS/S["+ "{total} Total item count,"+ "{totalfiles} Total file count,"+ "{totaldirs} Total dir count,"+ "{totalsel} Selected item count,"+ "{totalselfiles} Selected file count,"+ "{totalseldirs} Selected dir count"+ "],"+ "PT/R,"+ //fromclip "FROMCLIP/O[quiet],"+ "SAFEMODE/S,"+ "COMMANDS/R," + //recurse "RECURSE/O,"+ "PATH/K,"+ //"PATTERN/K,"+ //"FROM/K,TO/K,"+ "TYPE/K[dirs,files],"+ "REGEXP/S,"+ //"SAFEMODE/S,"+ //shared misc "FILEINFO/S,NODESELECT/S,XLOG/O[off,xit,error,warning,info,,trace,dump,all]"; /////////////////////////////////////////////////////////////////////////////// function OnInit(data){ //uid added via script wizard (do not change after publishing this script) var uid = "641228BF-DBE7-4C09-BE38-6B229EDCB3A3"; //resource center url added via script wizard (required for updating) var url = "http://resource.dopus.com/viewtopic.php?f=35&t=24543"; data.name = "Command.Generic: RenameEx"; data.log_prefix = "RenameEx"; data.desc = "Extended Rename command."; data.copyright = "tbone"; data.version = "0.3.3"; data.min_version = "11.5"; data.default_enable = true; var cmd = data.AddCommand(); cmd.name = "RenameEx"; cmd.method = "Command_RenameEx"; cmd.desc = data.desc; cmd.label = "RenameEx"; cmd.template = template; /////////////////////////////////////////////////////////////////////////// function ConfigHelper(data){ //v1.2 var t=this; t.d=data; t.c=data.config; t.cd=DOpus.Create.Map(); t.add=function(name, val, des){ t.l={n:name,ln:name. toLowerCase()}; return t.val(val).des(des);} t.des=function(des){ if (!des) return t; if (t.cd.empty) t.d.config_desc=t.cd; t.cd(t.l.n)=des; return t;} t.val=function(val){ var l=t.l; if (l.v!==l.x&&typeof l.v=="object") l.v.push_back(val);else l.v=t.c[l.n]=val;return t;} t.trn=function(){return t.des(t("script.config."+t.l.ln));}} /////////////////////////////////////////////////////////////////////////// var cfg = new ConfigHelper(data); cfg.add('XLog', DOpus.Create.Vector()). val(5).val("Off").val("Exception").val("Error").val("Warning").val("Info").val("Normal").val("Trace").val("Dump"). des('Console output level. The higher, the more output will be visible in the console window. '); } /////////////////////////////////////////////////////////////////////////////// var COMMAND_FAILURE = true; var COMMAND_USERABORT = true; var COMMAND_BADPARAMS = true; var COMMAND_SUCCESS = false; // ############################################################################ // # prototypes // ############################################################################ String.prototype.getFilePart = function (){ return this.split("\\").slice(-1)[0]; } // ############################################################################ String.prototype.getParentPart = function (){ var arr = this.split("\\"); arr.pop(); //remove last element return arr.join("\\"); } // ############################################################################ String.prototype.getExtension = function (){ return this.getFilePart().split(".").slice(-1)[0]; } // ############################################################################ String.prototype.getBaseName = function (){ var arr = this.getFilePart().split("."); if (arr.length==1) return arr[0]; arr.pop(); return arr.join("."); } String.prototype.lTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp("^"+chr+"*"),''));} String.prototype.rTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp(chr+"*$"),''));} String.prototype.trim = function(chr){return this.lTrim(chr).rTrim(chr);} String.prototype.left = function(chrs){return this.substring(0,chrs);} String.prototype.right = function(chrs){return this.substring(this.length-chrs);} /////////////////////////////////////////////////////////////////////////////// function Command_RenameEx(data){ var args = ArgsMagic(data, null, template), result = null; if (args["XLOG"].exists) XLog = args["XLOG"].value; Log("CmdLine: " + data.cmdline,"D"); if (args["NODESELECT"].exists) data.func.command.deselect = false; if (0); else if (args["NUMBER"].exists) return RenameEx_NUMBER(data.func.command, data.func.sourcetab, args); else if (args["FROMCLIP"].exists) return RenameEx_FROMCLIP(data.func.command, data.func.sourcetab, args); else if (args["RECURSE"].exists) return RenameEx_RECURSE(data.func.command, data.func.sourcetab, args); if (result===null){ Log("Bad/incomplete params.","X"); return COMMAND_BADPARAMS; } return result; } /////////////////////////////////////////////////////////////////////////////// function RenameEx_RECURSE(cmd, sourcetab, args) { Log("RenameEx_RECURSE():","T",1); /////////////////////////////////////////////////////////////////////////// function GetFolderItemsRecursiveSortedByDeepness(folderEnum){ var items = []; while (!folderEnum.complete){ var item = folderEnum.next(); var path = item.realpath+""; //remove drive letter or unc location from item path path = path.replace( /(.:\\|\\\\.*?\\.*\\)(.*)/g, "$2"); //split by "\" to determine deepness var level = path.split("\\").length; items[items.length] = {level:level, item:item }; } //sort items by level (path deepness), deepest items first //this is because renaming directories "at the top" makes it hard to rename //items further below, as their path gets invalid when renaming their container/parent items.sort(function(a,b) { if (a.level < b.level) return 1; if (a.level > b.level) return -1; return 0; }); return items; } /////////////////////////////////////////////////////////////////////////// var fm = new FSMagic(); var sourcePath = sourcetab.path+""; if (sourcetab.selected_dirs.count) sourcePath = sourcetab.selected_dirs(0).realpath; if (args["PATH"].exists) sourcePath = args["PATH"].value; Log("SAFEMODE : " + args["SAFEMODE"].exists,"D"); Log("PATH : " + sourcePath,"D"); var folderEnum = fm.ReadFolder(sourcePath, true, false); if (!folderEnum){ Log("Error reading folder ["+sourcePath+"].", "E"); return COMMAND_FAILURE; } var items = GetFolderItemsRecursiveSortedByDeepness(folderEnum); cmd.ClearFiles(); for(var i=0;iselItems.count || selItems.enumerator.atEnd()) break; var selItem = selItems.enumerator.item(); selItems.enumerator.moveNext(); var newItem = DOpus.FSUtil.NewPath(newItems[c]); for(var ce=0;ce padding) return value; len = padding-len+1; return (padding?new Array(len).join(padStr)+value:value); } /////////////////////////////////////////////////////////////////////////// function ReplaceVars(str,vars,padding,padWith){ return ""+ str.replace('{total}', Pad(vars.total, padding, padWith)). replace('{totalfiles}', Pad(vars.totalfiles, padding, padWith)). replace('{totaldirs}', Pad(vars.totaldirs, padding, padWith)). replace('{totalsel}', Pad(vars.totalsel, padding, padWith)). replace('{totalselfiles}', Pad(vars.totalselfiles, padding, padWith)). replace('{totalseldirs}', Pad(vars.totalseldirs, padding, padWith)); } /////////////////////////////////////////////////////////////////////////// var vars = { total : sourcetab.all.count, totalfiles : sourcetab.files.count, totaldirs : sourcetab.dirs.count, totalsel : sourcetab.selected.count, totalselfiles : sourcetab.selected_files.count, totalseldirs : sourcetab.selected_dirs.count } /////////////////////////////////////////////////////////////////////////// var padding = args["PAD"].exists ? "auto" : "0"; var padding = args["PAD"].options["size"]!=undefined ? args["PAD"].options["size"] : padding; var padWith = args["PAD"].options["with"]!=undefined ? args["PAD"].options["with"] : "0"; var to = args["TO"].exists ? args["TO"].value : "{stem} {num}{ext}"; var pattern = args["PATTERN"].exists ? args["PATTERN"].value : "(.*?)((?:[^A-z0-9]*|\\s*)?\\D?\\d+.*?)?($|\\..*)"; padding = ((""+padding).toLowerCase()=="auto"?(""+vars.totalsel).length:padding)*1; //remove native NUMBER switch from commandline, as it makes the rename //malfunction if no numbering shall take place (just adding {total..} e.g.) if (to.indexOf("{num}")==-1){ args["NUMBER"].exists = false; args["NUMBER"].value = ""; } Log("NUMBER : " + args["NUMBER"].value,"D"); Log("PATTERN : " + pattern,"D"); Log("FROM : " + args["FROM"].value,"D"); Log("TO : " + to,"D"); Log("PAD size: " + padding,"D"); Log("PAD with: " + padWith,"D"); var cmdline = 'Rename PATTERN="'+pattern+'" '+ ('TO="'+ReplaceVars(to, vars, padding, padWith)+'" '). replace('{stem}','\\1'+(!args["REMOVENUM"].exists?'\\2':'')). replace('{num}','[#'+padding+']'). replace('{ext}','\\3')+ 'REGEXP'+ (args["NUMBER"].exists && args["NUMBER"].value==""?' NUMBER':'')+ (args["NUMBER"].exists && args["NUMBER"].value!=""?' NUMBER="'+args["NUMBER"].value+'"':'')+ (args["FILEINFO"].exists?' FILEINFO':'')+ (args["FROM"].exists?' FROM="'+args["FROM"].value+'"':'')+ (args["BY"].exists?' BY="'+args["BY"].value+'"':'')+ (args["PT"].exists?' '+args["PT"].value:''); Log("Cmdline: " + cmdline,"D"); cmd.RunCommand(cmdline); Log("-","T",-1); return COMMAND_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// function TextMagic(){ this.version = 0.1; /////////////////////////////////////////////////////////////////////////// this.TextToFilePaths = function( text ){ var lineEnding = this.GetLineEndingOfText(text); if (lineEnding!=""){ var itemNames = text.split(lineEnding); } else var itemNames = new Array(text); return this.CleanFilePaths(itemNames) } /////////////////////////////////////////////////////////////////////////// this.TextToDOItems = function( text ){ var items = []; var filePaths = this.TextToFilePaths(text); for(var i=0;i"); if (parts.length>1) opValue = parts.slice(1).join(""); this.options[opName] = opValue; } if (!(!this.value && subOpsTemplate)) return; if (!(match = subOpsTemplate.match(/\<(.+?)\>/))) return; this.options[match[1]] = true; this.value = match[1]; } /////////////////////////////////////////////////////////////////////////// var types = {s:"switch",k:"keyword",o:"optional",n:"numeric",m:"multiple",r:"raw"}; var names = template.replace(/\/(.)\,/g,"/$1;").replace(/\]\,/g,"];").split(";"), args = []; for(arg in names) { var parts = names[arg].split("/"); var name=parts[0].toUpperCase(), subOpsTemplate=null; var parts2 = parts[1].split("["); var type=parts2[0].toLowerCase(); if (parts2.length>1) subOpsTemplate = parts2[1].substring(0,parts2[1].length-1); var exists=(data&&data.func.args.got_arg[name])||(jsonData&&(jsonData[name]!=undefined))||false; var value = (data && data.func.args[name]) || (jsonData && jsonData[name]) || undefined; args.push( args[name] = new Arg(name, types[type], exists, value, subOpsTemplate )); } /////////////////////////////////////////////////////////////////////////// args.Get = function( argName, configName, noneValue){ var arg = this[argName = argName.toUpperCase()]; if (!arg) throw new Error(0, "Unknown parameter ["+argName+"]"); if (arg.exists) return arg.value; //DOpus.Output("argName: " + argName); //DOpus.Output("configName: " + configName); if (!configName) return noneValue; if (typeof Script.config[configName] == "string") return new String(""+Script.config[configName]); return Script.config[configName]; } return args; } /////////////////////////////////////////////////////////////////////////////// function DumpObject(o, name, upperCaseProps, self, level){ this.version = 0.4; name = name || "unnamed"; if (self == undefined) DOpus.Output("DumpObject(): ["+name+"]"); var maxLen = 0; upperCaseProps = upperCaseProps || false; self = self || false; level = level || " "; for(prop in o) if (prop.length > maxLen) maxLen = prop.length; for(prop in o) { var pad = ""; while (pad.length+prop.length Log("text, "", 1); ) var u="undefined",lvs={o:0,x:1,e:2,w:3,i:4,n:5,t:6,d:7,a:8},i=["","X","E","W","I","","","",""],d=DOpus; if (typeof XLog==u){var v=d.Vars; if (v.Exists("XLog") && typeof XLogForce==u) {XLog=v.Get("XLog"); }} if (typeof XLog==u){try{var c=Script.Config;if(typeof c.XLog!=u){XLog=c.XLog;}}catch(e){d.Output("XLog: Early call, log-level=all.",1);XLog=8;}} if (typeof XLog==u){XLog="normal";} if(XLog.paused===undefined){ if(XLog===true) var L=5; else if(!isNaN(parseInt(XLog,10))) var L=XLog*1; else var L=lvs[(""+XLog).toLowerCase().substring(0,1)]; XLog={paused:false,I:0,L:L};} lvl=((typeof lvl==u || lvl == "")?5:lvs[lvl.substring(0,1).toLowerCase()]); //DOpus.Output("lvl=" + lvl + " XLog.L: " + XLog.L + " text: " + text); if (!(lvl && XLog.L && !XLog.paused && (lvl<=XLog.L))){ return;} // DOpus.Output("ok!" + lvl); var pad = (XLog.I==0?"":new Array(XLog.I+1).join(" ")); if (i[lvl]) pad = i[lvl]+(!pad?" ":pad.substring(1)); if (text!="-"){ if (d.Version.AtLeast("11.13.1")) d.Output(pad+text,((lvl==1||lvl==2)?1:0)); else d.Output(pad+text); } // DOpus.Output("end"); ind=(ind!==undefined?ind:0);XLog.I+=ind;if(XLog.I<0)throw new Error("XLog indent went sub-zero."); } /////////////////////////////////////////////////////////////////////////////// function OnAboutScript(data){ //v0.1 var cmd = DOpus.Create.Command(); if (!cmd.Commandlist('s').exists("ScriptWizard")){ if (DOpus.Dlg.Request("The 'ScriptWizard' add-in has not been found.\n\n"+ "Install 'ScriptWizard' from [resource.dopus.com].\nThe add-in enables this dialog and also offers "+ "easy updating of scripts and many more.","Yes, take me there!|Cancel", "No About.. ", data.window)) cmd.RunCommand('http://resource.dopus.com/viewtopic.php?f=35&t=23179');} else cmd.RunCommand('ScriptWizard ABOUT WIN='+data.window+' FILE="'+Script.File+'"'); } //MD5 = "98d7e531b548584907f586b00be2cbd9"; DATE = "2025.12.26 - 11:48:21"