<?xml version="1.0" encoding="UTF-8"?>
<rename_preset autorename="yes" case="none" ignoreext="yes" script="yes" type="normal" version="13">
	<from>*</from>
	<to>#MARKER#</to>
	<script>@script jscript
/************************** Script Information **************************
&apos; Rename from clipboard or file v1.0
&apos; Rewrite in Jscript by bytim 2025-07-28
&apos; URL: https://resource.dopus.com/t/rename-from-clipboard-or-file-jscript-version/56639

&apos; This rename preset take lines from clipboard or a text file and use them as new filenames or as part of new filenames.

&apos; Changes：
&apos; ● Adjusted some custom field options.
&apos; ● Added a new option: If only one line of text is obtained, apply it to all selected files.
&apos; ● Auto-detect the encoding of text file, using DOpusScriptingExtensions.UCharDet
&apos; 	https://resource.dopus.com/t/dopus-scripting-extensions-project-wild-idea/55000

&apos; Command for using in buttons:
&apos; ● From clipboard: (you can copy files or text to clipboard.)
	Rename PRESET=&quot;From Clipboard Or File&quot;
&apos; ● From text file:
	Rename PRESET=&quot;From Clipboard Or File&quot; SCRIPTARG useClip:false file:&quot;C:\test.txt&quot;

&apos; Original version (vbs): https://resource.dopus.com/t/from-clipboard-or-file-v4-1/48713
&apos; (tested with Directory Opus 13.1 x64)
&apos; by Henrik Ripa (henrik@gieron.info) 2024-02-05

&apos; ***********************************************************************/

var fnsSourceLines = DOpus.Create.Vector, 
	fnsCurrentLine, focFileNameUsed, dorInitialized = false;
var fsu = DOpus.FSUtil;

function OnGetCustomFields(getFieldData)
{
	getFieldData.field_labels(&quot;marker&quot;) = DOpus.strings.Get(&quot;marker&quot;);
	getFieldData.fields.marker = &quot;#MARKER#&quot;;
	
	//if(!optClipboardMode){
		getFieldData.field_labels(&quot;file&quot;) = DOpus.strings.Get(&quot;file&quot;);
		getFieldData.field_tips(&quot;file&quot;) = DOpus.strings.Get(&quot;file_tip&quot;);
		getFieldData.fields.file = &quot;filenames.txt&quot;;
	//}

	getFieldData.field_labels(&quot;useClip&quot;) = DOpus.strings.Get(&quot;useClip&quot;);
	getFieldData.fields.useClip = true;

	getFieldData.field_labels(&quot;one2all&quot;) = DOpus.strings.Get(&quot;one2all&quot;);
	getFieldData.fields.one2all = true;

	getFieldData.field_labels(&quot;trim_spaces&quot;) = DOpus.strings.Get(&quot;trim_spaces&quot;);
	getFieldData.fields.trim_spaces = true;

	getFieldData.field_labels(&quot;useEmptyLine&quot;) = DOpus.strings.Get(&quot;useEmptyLine&quot;);
	getFieldData.fields.useEmptyLine = false;
	
	getFieldData.field_labels(&quot;skipEmptyLine&quot;) = DOpus.strings.Get(&quot;skipEmptyLine&quot;);
	getFieldData.fields.skipEmptyLine = false;

	getFieldData.field_labels(&quot;reverse&quot;) = DOpus.strings.Get(&quot;reverse&quot;);
	getFieldData.fields.reverse = false;
}

//It is called once for each file that will be renamed.
function OnGetNewName(getNewNameData)
{
	var optClipboardMode = getNewNameData.custom.useClip;
	var file = &quot;&quot;;
	if(!optClipboardMode){
		file = getNewNameData.custom.file;
		if(file == &quot;&quot;) return true;
	}

	if(!dorInitialized){ //Only perform once.
		dorInitialized = true;
		
		//Initialize the file names source 获取新文件名赋值给vector数组对象fnsSourceLines
		fnsInitialize(getNewNameData.item.path, file, optClipboardMode, getNewNameData.custom.reverse);
	}
	
	var result = dorGetNewName(getNewNameData.item.name, 
							getNewNameData.newname, 
							getNewNameData.custom.marker, 
							getNewNameData.custom.useEmptyLine,  //原版变量optUseEmptyFilenames
							getNewNameData.custom.skipEmptyLine, 
							getNewNameData.custom.trim_spaces, 
							getNewNameData.custom.one2all);
	//log(&quot;Final name：&quot; + result);
	if(result == &quot;&quot;) return true;
	return result;
}

function fnsInitialize(strPath, strFileName, boolUseClipboard, boolReverseOrder){
	fnsCurrentLine = 0;
	var text = focGetText(strPath, strFileName, boolUseClipboard);
	if(text == &quot;&quot;) return;
	
  	var textArr = text.replace(/\r\n/g,&quot;\n&quot;).replace(/\r/g,&quot;\n&quot;).split(&quot;\n&quot;);
	fnsSourceLines = DOpus.Create.Vector(textArr);
	if(boolReverseOrder)
		fnsSourceLines.reverse(); //反序
	
	//for(var i=0; i&lt;fnsSourceLines.count; i++){log(i + &quot; &quot; + fnsSourceLines(i))}
	//log(&quot;Lines count: &quot; + fnsSourceLines.count);
}

function dorGetNewName(strOldName, strMidName, strMagicMarker, boolUseEmptyLines, boolSkipEmptyLines, boolTrimSpaces, boolOneToAll){
	//log(strOldName + &quot; /// &quot; + strMidName + &quot; /// &quot; + strMagicMarker);
	if (fnsNoMoreNames()) {log(DOpus.strings.Get(&quot;noMoreText&quot;)); return strOldName;}
	if (strMidName.indexOf(strMagicMarker) == -1) {log(DOpus.strings.Get(&quot;NoMarker&quot;)); return strOldName;}
	if (strOldName == focFileNameUsed) {log(DOpus.strings.Get(&quot;ForReading&quot;) + &apos;&quot;&apos; + focFileNameUsed + &apos;&quot;&apos;); return strOldName;}
	
	var line = fnsGetNextName(boolSkipEmptyLines, boolTrimSpaces, boolOneToAll);
	//log(&quot;Current line text：&quot; + line + &quot;  Original name：&quot; + strOldName);
	if (boolUseEmptyLines || line != &quot;&quot;)
		return strMidName.replace(strMagicMarker, line);
	else return strOldName;
}

function fnsNoMoreNames(){
	if (fnsCurrentLine &gt;= fnsSourceLines.count){
		return true;
	}
	return false;
}

function fnsGetNextName(boolSkipEmptyLines, boolTrimSpaces, boolOneToAll){
	//log(&quot;line &quot;+ (fnsCurrentLine+1) + &quot;/&quot; + fnsSourceLines.count)
	var fnsGetName = &quot;&quot;;
	if(boolOneToAll &amp;&amp; fnsSourceLines.count == 1)
		fnsGetName = fnsSourceLines(0);
	else {
		if (fnsCurrentLine &gt;= fnsSourceLines.count){
			log(DOpus.strings.Get(&quot;noMoreText&quot;));
			return &quot;&quot;;
		} else {
			fnsGetName = fnsSourceLines(fnsCurrentLine);
			fnsCurrentLine++;
			//Clean file name from unwanted characters
			fnsGetName = IllegalReplace(fnsGetName);
			if(boolTrimSpaces) //Trim and collapse spaces in file name
				fnsGetName = fnsGetName.replace(/\s+/g, &quot; &quot;).trim();
	
			if(boolSkipEmptyLines &amp;&amp; fnsGetName == &quot;&quot;)
				fnsGetName = fnsGetNextName(boolSkipEmptyLines, boolTrimSpaces, boolOneToAll); //递归调用自身
		}
	}
	return fnsGetName;
} 
			
//Reads text from file or from clipboard.
function focGetText(strPath, strFileName, boolUseClipboard){
	var text = &quot;&quot;;
	focFileNameUsed = &quot;&quot;
	if (boolUseClipboard){
		text = GetTextFromClip();
		if(text) log(DOpus.strings.Get(&quot;clipUse&quot;));
		else log(DOpus.strings.Get(&quot;clipNoText&quot;))
	} else {
		var filepath = strFileName;
		if(!fsu.Exists(filepath)){ // is not a absolute path.
			focFileNameUsed = strFileName;
			filepath = strPath + &quot;\\&quot; + strFileName; //Assuming the file is in the source path.
		}
		if(!fsu.Exists(filepath)){
			log(DOpus.strings.Get(&quot;FileNotExist&quot;));
			return &quot;&quot;;
		}
		
		text = GetTextFromFile(filepath); 
		if(text != &quot;&quot;)
			log(&apos;Read names from file: &quot;&apos; + filepath + &apos;&quot;&apos;);
		else log(&apos;The specified file does not contain valid text or cannot be read, nothing to use as source text&apos;);
	}

	return text.trim();
}

function GetTextFromClip(){
	var text = &quot;&quot;;
	if(DOpus.GetClipFormat() == &quot;files&quot;){
		var items = DOpus.GetClip(&quot;files&quot;);
		for (var eSel = new Enumerator(items); !eSel.atEnd(); eSel.moveNext()){
			var x = eSel.item();
			if(x.display_name != &quot;&quot; &amp;&amp; x.display_name != x.name)
				text = text + &quot;\n&quot; + x.display_name;
			else
				text = text + &quot;\n&quot; + x.name_stem_m;
		}
	} else text = DOpus.GetClip(&quot;text&quot;);
	
	//log(text);
	return text;
}

function GetTextFromFile(strTextFile){
	var textContent = &quot;&quot;;
	var encoding = &quot;UTF-8&quot;; // As default.
	var result = GetEncoding(strTextFile);
	if (result.err){
		log(result.msg + &quot; = &lt;&quot; + strTextFile + &quot;&gt;&quot;);
		return &quot;&quot;;
	} else encoding = result.encoding;
	
	if (encoding.toLowerCase() != &quot;unknown&quot; &amp;&amp; encoding.toLowerCase().indexOf(&quot;utf-32&quot;) == -1 ) {
		//textContent = DOpus.Create.StringTools.Decode(blob, encoding);//支持的编码少，不支持utf-32，改用ActiveXObject(&apos;Adodb.Stream&apos;)
		//DOpus.Create.StringTools.Decode(blob, 936);//这里GB18030要改用代码页cp936的数值，同样繁体中文是950  &quot;utf-8&quot;
		//var arr =blob.ToArray();
		var stream = new ActiveXObject(&apos;Adodb.Stream&apos;);
		//stream.Mode=3;//ReadWrite=3, Read=1, Write=2
		stream.Type = 1;//Binary byte
		stream.Open();
		//stream.Write(arr);
		try {stream.loadfromfile(strTextFile);}
		catch (err) {stream.Close(); log(DOpus.strings.Get(&quot;FailedOpen&quot;)); return &quot;&quot;;}
	    //stream.Position = 0;
		stream.Type = 2;//text
	    stream.Charset = encoding;
	    textContent = stream.ReadText(-1); //stream.ReadText(2)读取前2字节 也可以判断是不是BOM
	    stream.Close();
	}

	return textContent;
}

//Detect the Encoding of the file						 
function GetEncoding(filePath, size){
	var result = { err:false, msg:&quot;&quot;, encoding:&quot;&quot;};

	if (typeof size != &quot;undefined&quot;){
		if(size == 0){ result.msg = DOpus.strings.Get(&quot;EmptyFile&quot;); result.err = true; return result;}
		else if(size &lt; 4){ result.msg = DOpus.strings.Get(&quot;SmallSize&quot;); result.err = true; return result;}
	}

	var file = fsu.OpenFile(filePath);
    if (file.error){ result.msg = DOpus.strings.Get(&quot;FailedOpen&quot;); result.err = true; return result;}
	
	var blob = DOpus.Create.Blob(0,0,0,0); //prefill with 4 zero bytes
	var bytesRead = file.Read(blob, 4); file.Close();
	if (bytesRead&lt;4){ result.msg = DOpus.strings.Get(&quot;SmallSize&quot;); result.err = true; return result;}

	if (0);
	else if (blob(0)==0xEF &amp;&amp; blob(1)==0xBB &amp;&amp; blob(2)==0xBF &amp;&amp; blob(3)!=0x00) result.encoding = &quot;UTF-8&quot;;//UTF-8 BOM
	else if (blob(0)!=0x00 &amp;&amp; blob(1)==0x00 &amp;&amp; blob(2)!=0x00 &amp;&amp; blob(3)==0x00) result.encoding = &quot;UTF-16&quot;;//UTF-16 LE NoBOM
	else if (blob(0)==0x00 &amp;&amp; blob(1)!=0x00 &amp;&amp; blob(2)==0x00 &amp;&amp; blob(3)!=0x00) result.encoding = &quot;UTF-16BE&quot;;//UTF-16 BE NoBOM
	else if (blob(0)==0xFF &amp;&amp; blob(1)==0xFE &amp;&amp; blob(2)!=0x00 &amp;&amp; blob(3)==0x00) result.encoding = &quot;UTF-16&quot;;//UTF-16 LE BOM
	else if (blob(0)==0xFE &amp;&amp; blob(1)==0xFF &amp;&amp; blob(2)==0x00 &amp;&amp; blob(3)!=0x00) result.encoding = &quot;UTF-16BE&quot;;//UTF-16 BE BOM
	else if (blob(0)==0xFF &amp;&amp; blob(1)==0xFE &amp;&amp; blob(2)==0x00 &amp;&amp; blob(3)==0x00) result.encoding = &quot;UTF-32BE&quot;;//UTF-32 BE BOM
	else if (blob(0)==0x00 &amp;&amp; blob(1)==0x00 &amp;&amp; blob(2)==0xFE &amp;&amp; blob(3)==0xFF) result.encoding = &quot;UTF-32&quot;;//UTF-32 LE BOM
	else {
		var uCharDet = &quot;-&quot;;
		
		try {uCharDet = new ActiveXObject(&quot;DOpusScriptingExtensions.UCharDet&quot;);} //https://github.com/PolarGoose/DOpus-Scripting-Extensions
		catch (err) {log(&quot;不存在COM对象：DOpusScriptingExtensions.UCharDet&quot;);}
		
		if(typeof(uCharDet) != &quot;string&quot;){
			var enc = uCharDet.DetectFileEncoding(filePath, 2048);
			if(enc == &quot;&quot;){
				log(&quot;Can not detect encoding of the file，use UTF-8 as default.&quot;); //无法检测字符编码，按UTF-8处理
				enc = &quot;UTF-8&quot;;
			} else if(enc == &quot;UHC&quot;)
				enc = &quot;EUC-KR&quot;;//韩语
			
			result.encoding = enc;
		}
	}

	return result;
}

//文件名无效字符处理
//文件名中不允许的字符: \/|&lt;&gt;:*?&quot;
//文件名最长为251个字符
function IllegalReplace(line){
	//line = line.replace(/^\s*/, &quot;&quot;);//删除前面的不可见字符
	//line = line.match(/^.+$/m)[0];//匹配一行
	//log(typeof(line));
	line = line.replace(/[\|\\\/]+/g, &quot;¦&quot;).replace(/:/g, &quot;;&quot;).replace(/&lt;/g, &quot;[&quot;).replace(/&gt;/g, &quot;]&quot;);
	line = line.replace(/[\*\?]+/g, &quot;·&quot;).replace(/&quot;+/g, &quot;&apos;&quot;);
	line = line.trim();//删除前后空格
	line = line.slice(0,255);//避免文件名过长
	return line.trim();
}

function log(str){DOpus.Output(str,false,true);}
String.prototype.trim = function(){return this.replace(/^[\s\uFEFF\xA0\u3000]+|[\s\uFEFF\xA0\u3000]+$/g, &apos;&apos;);}//给字符串加上trim方法

==SCRIPT RESOURCES
&lt;resources&gt;
    &lt;resource type=&quot;strings&quot;&gt;
        &lt;strings lang=&quot;english&quot;&gt;
			&lt;string id=&quot;useClip&quot; text=&quot;Use clipboard text as the file name, otherwise read names from the specified file.&quot; /&gt;
            &lt;string id=&quot;file&quot; text=&quot;File to get filenames from&quot; /&gt;
            &lt;string id=&quot;file_tip&quot; text=&quot;It must be located in the same directory as the
&apos; files that you want to rename, or use an absolute path.&quot; /&gt;
            &lt;string id=&quot;marker&quot; text=&quot;Text to replace with filename&quot; /&gt;
            &lt;string id=&quot;trim_spaces&quot; text=&quot;Trim and collapse spaces in filenames&quot; /&gt;
			&lt;string id=&quot;useEmptyLine&quot; text=&quot;If getting empty line, rename with an empty name; otherwise, do not rename.&quot; /&gt;
			&lt;string id=&quot;skipEmptyLine&quot; text=&quot;Skip empty filenames&quot; /&gt;
            &lt;string id=&quot;reverse&quot; text=&quot;Reverse the order of filenames.&quot; /&gt;
			&lt;string id=&quot;one2all&quot; text=&quot;If only one line of text is obtained, apply it to all selected files&quot; /&gt;
			&lt;string id=&quot;noMoreText&quot; text=&quot;No more names in source text.&quot; /&gt;
			&lt;string id=&quot;clipUse&quot; text=&quot;Using the clipboard as source text.&quot; /&gt;
			&lt;string id=&quot;clipNoText&quot; text=&quot;Nothing in clipboard to use as source text.&quot; /&gt;
			&lt;string id=&quot;FileNotExist&quot; text=&quot;Invalid file path for reading text.&quot; /&gt;
			&lt;string id=&quot;EmptyFile&quot; text=&quot;empty file.&quot; /&gt;
			&lt;string id=&quot;SmallSize&quot; text=&quot;too small.&quot; /&gt;
			&lt;string id=&quot;FailedOpen&quot; text=&quot;Fail to open file.&quot; /&gt;
			&lt;string id=&quot;NoMarker&quot; text=&quot;No replacement marker string included.&quot; /&gt;
			&lt;string id=&quot;ForReading&quot; text=&quot;Used to read content, not allow rename this file: &quot; /&gt;
        &lt;/strings&gt;
        &lt;strings lang=&quot;chs&quot;&gt;
			&lt;string id=&quot;useClip&quot; text=&quot;使用剪贴板文本作文件名，否则从指定文件读取文件名&quot; /&gt;
            &lt;string id=&quot;file&quot; text=&quot;读取文件名于此文件&quot; /&gt;
            &lt;string id=&quot;file_tip&quot; text=&quot;必须与被重命名的文件在同一路径下，或者使用绝对路径&quot; /&gt;
            &lt;string id=&quot;marker&quot; text=&quot;文本替换标记&quot; /&gt;
            &lt;string id=&quot;trim_spaces&quot; text=&quot;去除首尾空格，合并连续空格&quot; /&gt;
			&lt;string id=&quot;useEmptyLine&quot; text=&quot;遇到空行 改成空文件名，否则不改名&quot; /&gt;
			&lt;string id=&quot;skipEmptyLine&quot; text=&quot;跳过空行&quot; /&gt;
            &lt;string id=&quot;reverse&quot; text=&quot;文件名行 反序&quot; /&gt;
			&lt;string id=&quot;one2all&quot; text=&quot;获取文本结果仅有一行时，用它重命名所有选中文件&quot; /&gt;
			&lt;string id=&quot;noMoreText&quot; text=&quot;源文本中已无更多名称&quot; /&gt;
			&lt;string id=&quot;clipUse&quot; text=&quot;使用剪贴板作为源文本&quot; /&gt;
			&lt;string id=&quot;clipNoText&quot; text=&quot;剪贴板中无文本可用&quot; /&gt;
			&lt;string id=&quot;FileNotExist&quot; text=&quot;指定的文件路径无效&quot; /&gt;
			&lt;string id=&quot;EmptyFile&quot; text=&quot;空文件&quot; /&gt;
			&lt;string id=&quot;SmallSize&quot; text=&quot;太小&quot; /&gt;
			&lt;string id=&quot;FailedOpen&quot; text=&quot;打开文件失败&quot; /&gt;
			&lt;string id=&quot;NoMarker&quot; text=&quot;不含替换占位标记字符串&quot; /&gt;
			&lt;string id=&quot;ForReading&quot; text=&quot;已用于读取内容，不允许重命名此文件: &quot; /&gt;
        &lt;/strings&gt;
    &lt;/resource&gt;
&lt;/resources&gt;</script>
	<custom>
		<var name="file" type="8">filenames.txt</var>
		<var name="marker" type="8">#MARKER#</var>
		<var name="one2all" type="11">yes</var>
		<var name="reverse" type="11">no</var>
		<var name="skipEmptyLine" type="11">no</var>
		<var name="trim_spaces" type="11">yes</var>
		<var name="useClip" type="11">yes</var>
		<var name="useEmptyLine" type="11">no</var>
	</custom>
</rename_preset>
