///////////////////////////////////////////////////////////////////////////////
var template =
	//main features..
	"FROMCLIP/S,FROMFILE/K,FROMVAR/O,FROMVARDROP/S,TOCLIP/S,TOFILE/K,TOVAR/O,CLEARVAR/K,ITEM/K,NTH/K,RANDOM/S,LINEAR/S,AUTO/S,SIMILARMETA/K,"+
	"SIMILARMETAJS/K,FILTERDROP/O[<select>,deselect,hide,hidenomatch],FILTERLIST/O[<select>,deselect,hide,hidenomatch],MAKEVISIBLE/O[checked],"+
	//fromfile, fromclip, fromvar, fromvardrop
	"SKIPITEMSWITHPATH/S,SKIPITEMSWITHOUTPATH/S,EXACT/S,GO/S,GOPT/R,"+
	//fromfile, fromclip, fromvar, item
	"USEPATH/S,"+
	//tofile, tovar, toclip
	"APPEND/S,ENCODING/K[unicode,ascii,default],EOL/O,"+
	//similarmetajs
	"INDEST/S,"+
	//random, linear
	"PATH/K,RECURSE/S,SETVAR/K,SETENV/K,FOLDERS/O[subfldmin,subfldmax,skipsubfld,depthmin,depthmax],FILES/S,SINGLEFOLDER/S,SINGLEFOLDERMINCOUNT/K[<1>],"+
	"ITEMCOUNTRND/S,ITEMCOUNT/K,ITEMCOUNTPERC/K,ACTION/K,NAME/K,MINSIZE/K,MAXSIZE/K,MAXSIZETOTAL/K,"+
	"JSFILTER/K,JSBREAK/K,ECHO/S,COPYTOCOLL/K,COMMANDS/R,"+
	//shared misc
	"DESELECTNOMATCH/S,NOFAIL/S,NODESELECT/S,XLOG/O[off,xit,error,warning,info,<normal>,trace,dump,all],PT/R"; //and MAKEVISIBLE
///////////////////////////////////////////////////////////////////////////////
function OnInit(data){
	//uid added via script wizard (do not change after publishing this script)
	var uid = "D6E6CF87-1FFB-4285-85F2-6C69727D2BD8";
	//resource center url added via script wizard (required for updating)
	var url = "http://resource.dopus.com/viewtopic.php?f=35&t=24414";
	data.name = "Command.Generic: SelectEx";
	data.log_prefix = "SelectEx";
	data.desc = "Extended Select command.";
	data.copyright = "tbone";
	if (DOpus.Version.AtLeast("12.0.8")) data.url = url;
	data.version = "0.5.8";
	data.min_version = "11.5";
	data.default_enable = true;

	var cmd = data.AddCommand();
    cmd.name = "SelectEx";
    cmd.method = "Command_SelectEx";
    cmd.desc = data.desc;
    cmd.label = "SelectEx";
	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").val("All").
		des('Console output level. The higher, the more output will be visible in the console window. ');
	cfg.add('AutoSelect',					false).
		des('Enable automatic selection of items.');
	cfg.add('AutoSelect.Actions',			"normal,dblclk").
		des('On what kind of folderchange to perform the automatic selection. You can use: normal, refresh, parent, root, back, forward, dblclk.');
	cfg.add('AutoSelect.MakeVisible',		true).
		des('Enable scrolling to automatically selected items.');
	cfg.add('AutoSelect.DeselectNoMatch',	true).
		des('Deselect items before automatic selection is applied (deselects automatically selected first file in folder if enabled e.g.).');
	cfg.add('AutoSelect.List',				DOpus.Create.Vector()).
		des('List of folder names and items to be selected automatically. '+
			'Paths and item names support regex if prefixed with "REGEX=", regular paths will be resolved (for aliases/env-vars).').
		val(';you can use ; or // or # to comment out lines').
		val(';syntax is: [REGEX=]<path> => [REGEX=]<item name>').
		val('').
		val(';standard path (select program files)').
		val('C:\\ => Program Files').
		val('').
		val(';alias for path and regex for item').
		val('/home => REGEX=dopus.exe|dopusrt.exe').
		val('').
		val(';env-var in path and regex for item (all log files)').
		val('%windir% => REGEX=.+\\.log$').
		val('').
		val(';using regex for the path').
		val('REGEX=.*\\\\drivers\\\\etc => hosts');
}
///////////////////////////////////////////////////////////////////////////////
var COMMAND_FAILURE = true;
var COMMAND_USERABORT = true;
var COMMAND_BADPARAMS = true;
var COMMAND_SUCCESS = false;
var fm = new FSMagic();
var gThis = this;
///////////////////////////////////////////////////////////////////////////////
String.prototype.lTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp("^"+chr+"*","i"),''));}
String.prototype.rTrim = function(chr){chr=chr||"\\s";return new String(this.replace(new RegExp(chr+"*$","i"),''));}
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_SelectEx(data){
	var args = ArgsMagic(data, null, template), result = null;

	//DumpObject(args); //return;

	if (args["XLOG"].exists){ 				XLog =		args["XLOG"].value; } Log("CmdLine: " + data.cmdline,"D");
	if (args["NODESELECT"].exists)			data.func.command.deselect = false;

	if (args["NOFAIL"].exists)				COMMAND_FAILURE				= COMMAND_SUCCESS;

	if (0);
	else if (args["AUTO"].exists)			return		SelectEx_AUTO(data.func.command, data.func.sourcetab, args);
	else if (args["SIMILARMETA"].exists)	return		SelectEx_SIMILARMETA(data.func.command, data.func.sourcetab, data.func.desttab, args);
	else if (args["SIMILARMETAJS"].exists)	return		SelectEx_SIMILARMETAJS(data.func.command, data.func.sourcetab, data.func.desttab, args);
	else if (args["FILTERDROP"].exists)		return		SelectEx_FILTERMENU(data.func.dlg, data.func.command, data.func.sourcetab, args, "dropdown");
	else if (args["FILTERLIST"].exists)		return		SelectEx_FILTERMENU(data.func.dlg, data.func.command, data.func.sourcetab, args, "multilist");
	else if (args["FROMCLIP"].exists)		return		SelectEx_FROMCLIP(data.func.command, data.func.sourcetab, args);
	else if (args["FROMFILE"].exists)		return		SelectEx_FROMFILE(data.func.command, data.func.sourcetab, args);
	else if (args["FROMVAR"].exists)		return		SelectEx_FROMVAR(data.func.command, data.func.sourcetab, args);
	else if (args["CLEARVAR"].exists)		return		SelectEx_CLEARVAR(data.func.command, data.func.sourcetab, args);
	else if (args["FROMVARDROP"].exists)	return		SelectEx_FROMVARDROP(data.func.dlg, data.func.command, data.func.sourcetab, args);
	else if (args["ITEM"].exists)			return		SelectEx_ITEM(data.func.command, args, null);
	else if (args["NTH"].exists)			return		SelectEx_NTH(data.func.command, data.func.sourcetab, args);
	else if (args["RANDOM"].exists)			return		SelectEx_RNDLINEAR(data.func.command, data.func.sourcetab, args, true);
	else if (args["LINEAR"].exists)			return		SelectEx_RNDLINEAR(data.func.command, data.func.sourcetab, args, false);
	else if (args["TOCLIP"].exists)			return		SelectEx_TOCLIP(data.func.command, data.func.sourcetab, args);
	else if (args["TOFILE"].exists)			return		SelectEx_TOFILE(data.func.command, data.func.sourcetab, args);
	else if (args["TOVAR"].exists)			return		SelectEx_TOVAR(data.func.command, data.func.sourcetab, args);
	else if (args["MAKEVISIBLE"].exists)	return		SelectEx_MAKEVISIBLE(data.func.command, data.func.sourcetab, args, null);

	if (result===null){
		Log("Bad/incomplete params.","X");
		return COMMAND_BADPARAMS;
	}

	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_MAKEVISIBLE(cmd, sourcetab, args, item){
	Log("SelectEx_MAKEVISIBLE():","T",1);

	///////////////////////////////////////////////////////////////////////////
	function SwitchCheckboxMode( truefalse, cmd, sourcetab){
		Log('SwitchCheckboxMode():',"T",1);
		var targetState = "off";
		if (truefalse) targetState = "on";
		cmd.RunCommand('Set CheckboxMode='+targetState);
		cmd.Clear();
		sourcetab.Update();
		var abort = 0;
		while ((truefalse != sourcetab.selstats.checkbox_mode) && (abort++ < 100)){
			Log('Setting CheckboxMode failed to set:' + sourcetab.selstats.checkbox_mode + " target:"+targetState,"E");
			DOpus.Delay(10);
			sourcetab.Update();
		}
		if (abort){
			Log('Setting CheckboxMode finally:' + sourcetab.selstats.checkbox_mode,"E");
			Log('Took: '+ abort*10 +' milliseconds!',"E");
		}
		Log("-","T",-1);
		return sourcetab.selstats.checkbox_mode;
	}

	cmd.ClearFiles();
	cmd.deselect = false;
	sourcetab.Update();
	//update tab for changed selection, fetch first selected item
	//and select it once more to make the MAKEVISIBLE switch work
//	for (var iEnum = new Enumerator(sourcetab.selected); !iEnum.atEnd();){
//		var item = iEnum.item(); //iEnum.moveNext();
//		DOpus.Output("    Running SelectEx MAKEVISIBLE.." + item.realpath);
//		cmd.RunCommand('SelectEx ITEM="'+item.realpath+'" EXACT MAKEVISIBLE');
//		break;
//	}
	/////////////////////////
	var wasCheckboxMode = (sourcetab.selstats.checkbox_mode==true);
	var isCheckboxMode = wasCheckboxMode;
	var numSelItems = sourcetab.stats.selitems;
	var numCheItems = sourcetab.stats.checkeditems;

	if (numCheItems+numSelItems==0){
		Log("Aborting, nothing selected","D"); Log("-", "T",-1);
		return COMMAND_SUCCESS;
	}

	var prioCBSelection = args["MAKEVISIBLE"].options["checked"] || false;

	Log("PRIOCHECKED : " + prioCBSelection,"D");
	Log("CHECKBOXMODE: " + isCheckboxMode,"D");

	if (isCheckboxMode && numSelItems && (prioCBSelection==false || numCheItems==0)){
		Log("Disabling checkbox mode to scroll for regular selection (has prio)","D");
		isCheckboxMode = SwitchCheckboxMode( false, cmd, sourcetab);
	}

	Log("Scrolling selected items into view for "+(isCheckboxMode ? "CheckboxMode" : "RegularMode"),"D");

	if (!item){
		var selectedItems = new Enumerator(sourcetab.selected);
		var firstSelected = selectedItems.item();
		item = new String(firstSelected.realpath);
	}
	//cmd.AddLine('Select "'+firstSelected.name+'" MAKEVISIBLE EXACT');
	//cmd.Run();

	Log("item        : " + item,"D");

	args["EXACT"] = {exists:true};
	args["USEPATH"] = {exists:true};
	SelectEx_ITEM(cmd, args, item+'')

	if (wasCheckboxMode && !isCheckboxMode){
		Log("Turning checkboxes back on","D");
		isCheckboxMode = SwitchCheckboxMode( true, cmd, sourcetab);
	}

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_RNDLINEAR(cmd, sourcetab, args, random){
	Log("SelectEx_RNDLINEAR():","T",1);
	var fso = new ActiveXObject("Scripting.FilesystemObject");
	///////////////////////////////////////////////////////////////////////////
	function FriendlySizeToBytes(friendlySize){
		var size = parseFloat(friendlySize=friendlySize.toLowerCase());
		if (isNaN(size)) return null;
		if (friendlySize.match(/(\d|\s)(b|bytes?)$/)) ;
		else if (friendlySize.match(/(kb|kilobytes?)$/)) size*=1024;
		else if (friendlySize.match(/(mb|megabytes?)$/)) size*=1024*1024;
		else if (friendlySize.match(/(gb|gigabytes?)$/)) size*=1024*1024*1024;
		else if (friendlySize.match(/(tb|terabytes?)$/)) size*=1024*1024*1024*1024;
		else if (friendlySize.match(/(pb|petabytes?)$/)) size*=1024*1024*1024*1024*1024;
		else if (friendlySize.match(/(eb|exabytes?)$/)) size*=1024*1024*1024*1024*1024*1024;
		else size = null;
		return size;
	}
	///////////////////////////////////////////////////////////////////////////
	function RegexToWildcardObj(arg){
		if (arg.exists)
			return DOpus.FSUtil.NewWild(arg.value,"rf");
		return null;
	}
	///////////////////////////////////////////////////////////////////////////
	function ValidateSizeParameter(arg){
		var value = arg.value;
		if (arg.exists && (value=FriendlySizeToBytes(arg.value))==null){
			Log(arg.name+" malformed.", "X");
			throw new Error(arg.name+" malformed.");
		}
		return value;
	}
	///////////////////////////////////////////////////////////////////////////
	function GetFilteredItemCollections(sourcetab, args){
		//////////////////////////////////////////////////////////////////////
		function ReadFolder(path, recurse, level){
			Log("    ["+path+"]..", "A");
			level = level || 0;
			var iEnum = DOpus.FSUtil.ReadDir(path, false);
			if (iEnum.error!=0 && iEnum.error!=18 && iEnum.error!=2 ){ //18 = empty folder, 2=drive root
				Log("ReadDir ["+folderpath+"] failed, fEnum.error ["+iEnum.error+"]", "X");
				throw new Error("ReadDir ["+folderpath+"] failed, fEnum.error ["+iEnum.error+"]");
			}
			while (!iEnum.complete){
				var item = iEnum.next(), itemSize = item.size.cy*10000;
				if ((args["MINSIZE"].exists || args["MAXSIZE"].exists || args["MAXSIZETOTAL"].exists) && item.is_dir && !item.got_size)
					itemSize = GetFolderSize(item.realpath);
				var result = AddItemWithFilter(item, itemSize, filteredItemCollections, args, level, SRFilter);
				if (item.is_dir && recurse)
					if (result !== FILTER_PASSED_SKIPSUBFOLDERS)
						ReadFolder(item.realpath, recurse, level+1);
			}
		}
		///////////////////////////////////////////////////////////////////////
		function AddItemWithFilter(item, itemSize, filteredItemCollections, args, level, SRFilter){
			var filRes = Filter(item, itemSize, args, level, SRFilter);

			switch(filRes){
				case FILTER_PASSED:					break;
				case FILTER_PASSED_SKIPSUBFOLDERS:	break;
				case "jsfilter_exception":			throw new Error("JSFilter failed, aborting.");
				default:							return false;
			}

			filteredItemCollections.all.push_back(item);
			if (item.is_dir) filteredItemCollections.dirs.push_back(item);
			else filteredItemCollections.files.push_back(item);

			if (filRes == FILTER_PASSED_SKIPSUBFOLDERS) return filRes;
			return true;
		}
		///////////////////////////////////////////////////////////////////////
		function Filter( item, itemSize, args, level, SRFilter){
			if (item.is_dir && args["PATH"].exists){
				var depthmin=null, depthmax=null;
				if (args["FOLDERS"].options["depthmin"] || args["FOLDERS"].options["depthmax"]){
					if (args["FOLDERS"].options["depthmin"]) depthmin = args["FOLDERS"].options["depthmin"]*1;
					if (args["FOLDERS"].options["depthmax"]) depthmax = args["FOLDERS"].options["depthmax"]*1;
					if (depthmin!==null && (level<depthmin)) return "depthmin";
					if (depthmax!==null && (level>depthmax)) return "depthmax"; //might never happen
					//Log("min: "+ args["FOLDERS"].options["depthmin"] +" max: " + args["FOLDERS"].options["depthmax"] + " lvl: " + level);
				}
				if (args["FOLDERS"].options["subfldmin"] || args["FOLDERS"].options["subfldmax"]){
					var fld = fso.GetFolder(item.realpath);
					var minCount=null, maxCount=null;
					if (args["FOLDERS"].options["subfldmin"]) minCount = args["FOLDERS"].options["subfldmin"]*1;
					if (args["FOLDERS"].options["subfldmax"]) maxCount = args["FOLDERS"].options["subfldmax"]*1;
					if (minCount!==null && (minCount>fld.SubFolders.count)) return "subfldmin";
					if (maxCount!==null && (maxCount<fld.SubFolders.count)) return "subfldmax";
					if (args["FOLDERS"].options["skipsubfld"]) return FILTER_PASSED_SKIPSUBFOLDERS;
				}
				if (depthmax!==null && ((level+1)>depthmax)) return FILTER_PASSED_SKIPSUBFOLDERS;
			}
			if (args["NAME"].exists && !args["NAME"].value.Match(item.name))	return "name";
			if (args["MINSIZE"].exists && args["MINSIZE"].value > itemSize)		return "minsize";
			if (args["MAXSIZE"].exists && args["MAXSIZE"].value < itemSize)		return "maxsize";

			try{ if (SRFilter && SRFilter(item)!==false) return "jsfilter";	}
				catch (e){ return "jsfilter_exception"; }
			return FILTER_PASSED;
		}
		//////////////////////////////////////////////////////////////////////
		var FILTER_PASSED=1;
		var FILTER_PASSED_SKIPSUBFOLDERS=3;

		var filteredItemCollections = {	all		: DOpus.Create.Vector(),
										files	: DOpus.Create.Vector(),
										dirs	: DOpus.Create.Vector()};
		var SRFilter = null;
		if (args["JSFILTER"].exists){
			try{
				var SRFilter = new Function("item", args["JSFILTER"].value);
			} catch(e){
				Log("Creating function from JSFilter failed: " + e,"X");
				throw new Error("Creating function from JSFilter failed: " + e);
			}
		}

		if (args["PATH"].exists){
			Log("Scanning and pre-filtering..", "D");
			if (!DOpus.FSUtil.Exists(args["PATH"].value)){
				Log("Path ["+args["PATH"].value+"] not found", "X");
				throw new Error("Path ["+args["PATH"].value+"] not found");
			}
			ReadFolder( args["PATH"].value, args["RECURSE"].exists);
		} else {
			Log("Sourcetab: " + sourcetab.path, "D");
			for (var iEnum = new Enumerator(sourcetab.all); !iEnum.atEnd();){
				var item = iEnum.item(), itemSize = item.size.cy*10000; iEnum.moveNext();
				if ((args["MINSIZE"].exists || args["MAXSIZE"].exists || args["MAXSIZETOTAL"].exists) && item.is_dir && !item.got_size)
					itemSize = GetFolderSize(item.realpath);
				AddItemWithFilter(item, itemSize, filteredItemCollections, args, 0 /*level*/, SRFilter);
			}
		}
		return filteredItemCollections;
	}
	///////////////////////////////////////////////////////////////////////////
	function SetResultVariables(sourcetab, args, value){
		if (args["SETVAR"].exists) sourcetab.vars.Set(args["SETVAR"].value, value);
		if (args["SETENV"].exists){ var EM = new EnvMagic(); EM.Set(args["SETENV"].value, value); }
	}
	///////////////////////////////////////////////////////////////////////////
	function PickItems(cmd, items, args){
		var maxItems		= args["ITEMCOUNT"].value;
		var maxSizeTotal	= args["MAXSIZETOTAL"].value;
		var random			= args["RANDOM"].exists;
		var jsBreak = null;

		Log("Picking items ("+items.count+" total)..","D");
		///////////////////////////////////////////////////////////////////////
		function MaxSizeReached(){
			if (!args["MAXSIZETOTAL"].exists) return false;
			if (item.is_dir && !item.got_size) itemSize = GetFolderSize(item.realpath);
			if (((sizeTotal+itemSize)>args["MAXSIZETOTAL"].value)) return true;
			return false;
		}
		///////////////////////////////////////////////////////////////////////
		cmd.ClearFiles();

		if (args["JSBREAK"].exists){
			try{ jsBreak = new Function("item", args["JSBREAK"].value); }
			catch(e){
				Log("Creating function from JSBreak failed: " + e,"X");
				throw new Error("Creating function from JSBreak failed: " + e);
			}
		}

		var sizeTotal = 0;
		if (random) {
			var randomsUsed = { "dummy":true };
			var r = "dummy";
			for (var i=0;i<maxItems && items.count;i++){
				r = Math.floor((Math.random()*items.count));
				var item = items(r), itemSize = item.size.cy*10000;
				items.erase(r);
				if (MaxSizeReached()) { i--; continue; }
				if (jsBreak){
					var res = jsBreak(item);
					if (res === true) break; //enough items
					if (res === false) { i--; continue; }; //skip this item
				}
				//select item
				sizeTotal+=itemSize;
				cmd.AddFile(item);
			}
		} else {
			for (var i=0;i<maxItems && i<items.count;i++){
				var item = items(i), itemSize = item.size.cy*10000;
				if (MaxSizeReached()) break;
				if (jsBreak){
					var res = jsBreak(item);
					if (res === true) break; //enough items
					if (res === false) continue; //skip this item
				}
				//select item
				sizeTotal+=itemSize;
				cmd.AddFile(item);
			}
		}
		return cmd;
	}
	///////////////////////////////////////////////////////////////////////////
	function GetNumberOfItemsToSelect(args, items){
		var itemsToSelect = args["ITEMCOUNT"].value;
		if (args["ITEMCOUNTPERC"].exists && !isNaN(parseInt(args["ITEMCOUNTPERC"].value,10))) {
			args["ITEMCOUNTPERC"].value = parseInt(args["ITEMCOUNTPERC"].value,10);
			if (args["ITEMCOUNTPERC"].value > 0)
				itemsToSelect = items.count*(args["ITEMCOUNTPERC"].value/100);
		}
		if (itemsToSelect > items.count) itemsToSelect = items.count;
		if (args["ITEMCOUNTRND"].exists){
			if (itemsToSelect > 1)
				itemsToSelect = Math.floor((Math.random()*itemsToSelect)+1);
			else
				itemsToSelect = Math.floor((Math.random()*items.count)+1);
		}
		return itemsToSelect;
	}
	///////////////////////////////////////////////////////////////////////////
	function JoinItemsToString(collection, separator){
		separator = separator || "\r\n";
		var itemsSeparated = "";
		for (var iEnum = new Enumerator(collection); !iEnum.atEnd();){
			var item = iEnum.item(); iEnum.moveNext();
			itemsSeparated += item.realpath + (!iEnum.atEnd()?separator:"");
		}
		return itemsSeparated;
	}
	///////////////////////////////////////////////////////////////////////////
	function GetCollectionToPickFrom(filteredItemCollections, args){
		if (args["SINGLEFOLDER"].exists && ((args["SINGLEFOLDERMINCOUNT"].value*1) <= filteredItemCollections.dirs.count)){
			args["ITEMCOUNTRND"].exists = false;
			args["ITEMCOUNT"].value = 1;
			args["ITEMCOUNTPERC"].exists = false;
			return filteredItemCollections.dirs;
		}
		if (args["FOLDERS"].exists && !args["FILES"].exists)	return filteredItemCollections.dirs;
		if (args["FILES"].exists  && !args["FOLDERS"].exists)	return filteredItemCollections.files;
		return filteredItemCollections.all;
	}
	///////////////////////////////////////////////////////////////////////////
	function SelectItemsInFiledisplay(args, cmd){
		if (!args["PATH"].exists){
			Log("Selecting items..","D");
			cmd.RunCommand("Select FROMSCRIPT EXACT"+
				(args["DESELECTNOMATCH"].exists?" DESELECTNOMATCH":"")+
				(args["MAKEVISIBLE"].exists?" MAKEVISIBLE":"")
				);
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////////////////////
	function SetResults(sourcetab, cmd, args){
		var content = null;
		if (args["SETVAR"].exists || args["SETENV"].exists || args["TOFILE"].exists){
			var eol = GetEndOfLine(args);
			content = JoinItemsToString(cmd.files, eol);
		}
		if (args["SETVAR"].exists || args["SETENV"].exists)
			SetResultVariables(sourcetab, args, content);
		if (args["TOFILE"].exists){
			var filePath = DOpus.FSUtil.Resolve(args["TOFILE"].value);
			var fm = new FSMagic();
			var file = fm.OpenFile(filePath, args["APPEND"].exists?"a":"w", args["ENCODING"].value, true);
			if (file!==null){
				file.WriteLine(content); file.Close();
			}
		}
		return (content!==null);
	}
	///////////////////////////////////////////////////////////////////////////
	function TriggerFileTypeActions(args, cmd){
		if (args["ACTION"].exists){
			Log("Triggering action ["+args["ACTION"].value+"]..","D");
			cmd.RunCommand("FileType ACTION="+args["ACTION"].value);
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////////////////////
	function ExecuteCommands(args, cmd){
		if (args["COMMANDS"].exists && cmd.files.count){
			Log("Running commands..","D");
			var commands = args["COMMANDS"].value.split("\\n");
			for(var c=0;c<commands.length;c++){
				Log("    Cmd: " + commands[c],"D");
				cmd.RunCommand(commands[c]);
			}
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////////////////////
	function EchoItemsToConsole(args, cmd){
		if (args["ECHO"].exists){
			var items = "Items ("+cmd.files.count+"):"+(cmd.files.count?"<br>":"");
			for (var iEnum = new Enumerator(cmd.files); !iEnum.atEnd();){
				var item = iEnum.item(); iEnum.moveNext();
				items += item.realpath;
				if (!iEnum.atEnd()) items += "<br>";
			}
			Log(items);
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////////////////////
	function CopyToCollection(args, cmd){
		if (args["COPYTOCOLL"].exists){
			var colName = args["COPYTOCOLL"].value;
			Log("Copying to collection ["+colName+"]..","D");
			var cmdCol = DOpus.Create.Command();
			cmdCol.AddLine('CreateFolder "coll://'+colName+'"');
			cmdCol.AddLine('Copy COPYTOCOLL=member TO "coll://'+colName+'"');
			var addedItems = false;
			for (var iEnum = new Enumerator(cmd.files); !iEnum.atEnd();){
				var item = iEnum.item(); iEnum.moveNext();
				if (!DOpus.FSUtil.Exists("coll://"+colName+"/"+item.name)){
					cmdCol.AddFile(item);
					addedItems = true;
				}
			}
			//as selected files in source will be used instead (bug?)
			//issue: http://resource.dopus.com/viewtopic.php?f=3&t=24406
			if (addedItems) cmdCol.Run();
			return true;
		}
		return false;
	}
	///////////////////////////////////////////////////////////////////////////
	function GetFolderSize(path){
		var size = fso.GetFolder(path).Size;
		if (!isNaN(parseInt(size,10))) return size;
		return null;
	}
	///////////////////////////////////////////////////////////////////////////

	if (!args["ITEMCOUNT"].exists) args["ITEMCOUNT"].value = 1;
	args["NAME"].value 			= RegexToWildcardObj(args["NAME"]);
	args["MINSIZE"].value		= ValidateSizeParameter(args["MINSIZE"]);
	args["MAXSIZE"].value		= ValidateSizeParameter(args["MAXSIZE"]);
	args["MAXSIZETOTAL"].value	= ValidateSizeParameter(args["MAXSIZETOTAL"]);

	DumpArgs(args);

	var filteredItemCollections	= GetFilteredItemCollections(sourcetab, args);
	var filteredItemCollection	= GetCollectionToPickFrom(filteredItemCollections, args);

	if (filteredItemCollection.count==0){
		SetResultVariables(sourcetab, args, "");
		Log("No items, aborting","I");
		return COMMAND_FAILURE;
	}

	args["ITEMCOUNT"].value 	= GetNumberOfItemsToSelect(args, filteredItemCollection);
	var pickedItemsCommand		= PickItems(cmd, filteredItemCollection, args); //params.itemCount, params.maxSizeTotal, params.jsBreak, random);
	var voidItemsCopied			= CopyToCollection(args, pickedItemsCommand); //if copytocoll given
	var voidItemsReturned		= SetResults(sourcetab, pickedItemsCommand, args); //if variable names given
	var voidItemsSelected		= SelectItemsInFiledisplay(args, pickedItemsCommand); //if no path given
	var voidActionsTriggered	= TriggerFileTypeActions(args, pickedItemsCommand); //if action given
	var voidCommandsExecuted	= ExecuteCommands(args, pickedItemsCommand); //if commands given
	var voidItemsEchoed			= EchoItemsToConsole(args, pickedItemsCommand); //if echo enabled

	Log("Done.","D"); Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function EnvMagic(scope){ //v0.1
	this.scope = scope || "Process";
	try{this.shell=shell} catch(e){this.shell=new ActiveXObject("WScript.Shell")};
	this.env = this.shell.Environment(this.scope);
	///////////////////////////////////////////////////////////////////////////
	this.Set = function (varname, value, scope){
		var env = scope?this.shell.Environment(scope):this.env;
		env(varname) = value;
		return value;
	}
	///////////////////////////////////////////////////////////////////////////
	this.Get = function (varname, scope){
		var env = scope?this.shell.Environment(scope):this.env;
		return env(varname);
	}
	///////////////////////////////////////////////////////////////////////////
	this.Remove = function (varname, scope){
		var env = scope?this.shell.Environment(scope):this.env;
		return env.Remove(varname);
	}
	///////////////////////////////////////////////////////////////////////////
	this.Expand = function (varString){
		return this.shell.ExpandEnvironmentStrings(varString);
	}
}
///////////////////////////////////////////////////////////////////////////////
function PrintParams( params){
	var maxLen = 0;
	for(param in params) if (param.length > maxLen)	maxLen = param.length;
	for(param in params){
		var pad = ""; while (pad.length+param.length<maxLen) pad+=" ";
		Log(param.toUpperCase()+pad+": "+params[param], "D");
	}
}
///////////////////////////////////////////////////////////////////////////////
function DumpArgs( args ){
	var maxLen = 0;
	for(arg in args) if (arg.length > maxLen)	maxLen = arg.length;
	for(arg in args){
		if (!isNaN(parseInt(arg,10))) continue; //skip numbered index
		var pad = ""; while (pad.length+arg.length<maxLen) pad+=" ";
		Log(arg.toUpperCase()+pad+": "+args[arg].value, "D");
	}
}
///////////////////////////////////////////////////////////////////////////////
function OnAfterFolderChange(data){
	Log("OnAfterFolderChange():", "T", 1);
	Log("data.action: " + data.action, "D");
	Log("cnf.actions: " + Script.Config["AutoSelect.Actions"], "D");
	var leave = false;
	if (!data.result) leave = true;
	else if (!Script.config["AutoSelect"]) leave = true;
	else if (Script.Config["AutoSelect.Actions"].indexOf(data.action)==-1) leave = true;

	if (leave){
		Log("Disabled or action mismatch, aborting.", "D");
		return;
	}

	var args = ArgsMagic(null, {}, template);
	if (Script.Config["AutoSelect.MakeVisible"])
		args["MAKEVISIBLE"] = {exists:true};
	if (Script.Config["AutoSelect.DeselectNoMatch"])
		args["DESELECTNOMATCH"] = {exists:true};
	SelectEx_AUTO(DOpus.Create.Command(), data.tab, args);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_AUTO(cmd, sourcetab, args){
	Log("SelectEx_AUTO(): (" + Script.config["AutoSelect"]+ ")","T",1);

	var list = Script.config["AutoSelect.List"];
	var tabPath = new String(sourcetab.path), selItems = [];

	for(var i=0;i<list.count;i++){
		if (list(i).substring(0,2)=="//"|| list(i).substring(0,1)=="#"||
			list(i).substring(0,1)==";" || list(i)=="") continue;
		var parts = new String(list(i)).split("=>");
		if (parts.length!=2) continue;

		Log("Entry: " + list(i),"D");
		var path = parts[0].trim();
		if (path.substring(0,6)=="REGEX="){
			path = path.substring(6);
		} else {
			path = path!=""?new String(DOpus.FSUtil.Resolve(path)):path;
			path = path.replace(/\\/g,'\\\\')+"$";
		}
		Log("    Path: " + path,"D");
		Log("    TabPath: " + tabPath,"D");
		if (!tabPath.match(new RegExp(path,"i"))){
			Log("    No path match","D");
			continue;
		}

		var item = parts[1].trim();
		selItems[selItems.length] = item;
	}
	if (selItems.length){
		Log("SelItems: " + selItems,"D");
		args["USEPATH"] = {exists:true};
		SelectArrayItems(cmd, sourcetab, args, selItems);
	}
	Log("-","T",-1);
	return;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_FILTERMENU(dlg, cmd, sourcetab, args, type){
	Log("SelectEx_FILTERMENU():","T",1);
	///////////////////////////////////////////////////////////////////////////
	function GetFilterNamesVector(){
		var filtersPath	= DOpus.FSUtil.Resolve("/dopusdata\\filters");
		var fEnum		= DOpus.FSUtil.ReadDir(filtersPath);
		if (fEnum.error!=0 && fEnum.error!=18 && fEnum.error!=2 ) //18 = empty folder, 2=drive root
			throw new Error(0,"ReadDir ["+filtersPath+"] failed, fEnum.error ["+fEnum.error+"]");
		var list = DOpus.Create.Vector();
		while (!fEnum.complete){
			var filterItem = fEnum.next();
			if (filterItem.is_dir == true) continue;
			if (filterItem.ext.toLowerCase() != ".ofi") continue;
			list.push_back(filterItem.name_stem);
		}
		return list;
	}
	///////////////////////////////////////////////////////////////////////////
	function OpenMenu(dlg, type, filters, filterFlags){
		dlg.title = "选择过滤器";
		dlg.message = "勾选过滤器来 "+filterFlags.replace(",",", ")+" 项目:"
		dlg.buttons = "确定|取消";
		dlg.choices = filters;
		if (type=="dropdown")	dlg.menu = 0;
		if (type=="multilist")	dlg.list = 0;
		dlg.Show();
		return dlg.result;
	}
	///////////////////////////////////////////////////////////////////////////
	function AddSelection(cmd, filterName, filterFlags){
		var cmdLine = 'Select "'+filterName+'" FILTER FILTERFLAGS="'+filterFlags+'"'+
						(args["MAKEVISIBLE"].exists?' MAKEVISIBLE':'')+
						(args["PT"].exists?' '+args["PT"].value:'')
						;
		cmd.AddLine(cmdLine);
		Log("Command: " + cmdLine,"D");
	}
	///////////////////////////////////////////////////////////////////////////

	Log("DESELECTNOMATCH: " + args["DESELECTNOMATCH"].exists,"D");
	Log("MAKEVISIBLE    : " + args["MAKEVISIBLE"].exists,"D");
	Log("PT             : " + args["PT"].value,"D");

	cmd.deselect = false;
	if (args["DESELECTNOMATCH"].exists){
		Log("cmd.AddLine(Select NONE);","D");
		cmd.AddLine('Select NONE');
	}

	if (type=="dropdown")	var filterFlags = args["FILTERDROP"].value;
	if (type=="multilist")	var filterFlags = args["FILTERLIST"].value;

	var filters = GetFilterNamesVector();
	if (!filters.count){
		Log("No filters found.","X"); Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var result = OpenMenu(dlg, type, filters, filterFlags);
	if (result==0){
		Log("User abort.","T",-1);
		return COMMAND_USERABORT;
	}

	if (type=="multilist")
		for(var c=0;c<dlg.choices.count;c++)
			if (dlg.list(c)) AddSelection(cmd, dlg.choices(c), filterFlags);

	if (type=="dropdown")
		AddSelection(cmd, dlg.choices(result-1), filterFlags);

	cmd.AddLine("//prevent exception if no line added");
	cmd.Run();

	sourcetab.vars.Set("Script.SelectEx.FilterEnabled", true);
	sourcetab.vars.Set("Script.SelectEx.FilterEnabled.bfc.drop", "always");
	cmd.UpdateToggle();

	Log("Done.","T",-1);
	return;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_FROMVARDROP(dlg, cmd, sourcetab, args){
	Log("SelectEx_FROMVARDROP():","T",1);
	///////////////////////////////////////////////////////////////////////////
	function GetVarNamesVector(){
		var list = DOpus.Create.Vector();
		//var varNameFull = "Script.SelectEx.StoredSelection."+varName;
		for (enumVarNames = new Enumerator(DOpus.Vars); !enumVarNames.atEnd();){
			var item = new String(enumVarNames.item()); enumVarNames.moveNext();
			if (item.indexOf("Script.SelectEx.StoredSelection.")==-1) continue;
			item = item.replace("Script.SelectEx.StoredSelection.","");
			if (item)
			list.push_back(item);
		}
		return list;
	}
	///////////////////////////////////////////////////////////////////////////
	function OpenMenu(dlg, varNames){
		//dlg.title = "Select Filters";
		//dlg.message = "Check filters to "+filterFlags.replace(",",", ")+" items:"
		//dlg.buttons = "Ok|Cancel";
		dlg.choices = varNames;
		dlg.menu = 0;
		//if (type=="multilist")	dlg.list = 0;
		dlg.Show();
		return dlg.result;
	}
	///////////////////////////////////////////////////////////////////////////

	Log("DESELECTNOMATCH: " + args["DESELECTNOMATCH"].exists,"D");
	Log("MAKEVISIBLE    : " + args["MAKEVISIBLE"].exists,"D");
	Log("PT             : " + args["PT"].value,"D");

	cmd.deselect = false;
	if (args["DESELECTNOMATCH"].exists){
		Log("cmd.AddLine(Select NONE);","D");
		cmd.AddLine('Select NONE');
	}

	var varNames = GetVarNamesVector();
	if (!varNames.count){
		//Request("No memorized selections found.")
		//Log("No memorized selections found.","X"); Log("-","T",-1);
		Log("No memorized selections found."); Log("-","T",-1);
		dlg.Request("没有保存过任何选集，需事先将选集保存到内存区。            ", "确定", "不可执行");
		return COMMAND_FAILURE;
	}

	var result = OpenMenu(dlg, varNames);
	if (result==0){
		Log("User abort.","T",-1);
		return COMMAND_USERABORT;
	}

	args["FROMVAR"].exists = true;
	args["FROMVAR"].value = dlg.choices(result-1);
	var result = SelectEx_FROMVAR(cmd, sourcetab, args);

	Log("Done.","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_SIMILARMETA(cmd, sourcetab, desttab, args){
	Log("SelectEx_SIMILARMETA():","T",1);
	//"metadata.image.picheight;metadata.image.picwidth"

	var metas = args.Get("SIMILARMETA", "", "").trim().split(";");
	Log("SIMILARMETA: " + metas,"D");
	Log("MAKEVISIBLE: " + args["MAKEVISIBLE"].exists,"D");
	for (var i=0;i<metas.length;i++){
		var meta = ""
		var parts = metas[i].split(".");
		for (var p=0;p<parts.length;p++)
			meta += parts[p].trim()+((p+1)<parts.length?'"]["':'');
		metas[i] = '["'+meta+'"]';
	}
	if (!metas || (metas && !metas.length)){
		Log("Parsing meta properties failed: " + e,"E");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}
	var result = SelectEx_SIMILAR(cmd, sourcetab, desttab, args, metas, null);
	Log("-","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_SIMILARMETAJS(cmd, sourcetab, desttab, args){
	Log("SelectEx_SIMILARMETAJS():","T",1);
	//"return selItem.name.left(8)==item.name.left(8);"

	var jsCompare = args.Get("SIMILARMETAJS", "", null);
	Log("SIMILARMETAJS: " + jsCompare,"D");
	Log("MAKEVISIBLE  : " + args["MAKEVISIBLE"].exists,"D");

	try{
		jsCompare = new Function("selItem", "selItems", "item", "cmd", jsCompare);
	} catch(e){
		Log("Creating function failed: " + e,"X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var result = SelectEx_SIMILAR(cmd, sourcetab, desttab, args, null, jsCompare);
	Log("-","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////
function levenshtein(str1, str2) {
	var current = [], prev, value;
	for (var i = 0; i <= str2.length; i++)
		for (var j = 0; j <= str1.length; j++) {
		if (i && j)
			if (str1.charAt(j - 1) === str2.charAt(i - 1))
				value = prev;
			else
				value = Math.min(current[j], current[j - 1], prev) + 1;
		else
			value = i + j;

		prev = current[j];
		current[j] = value;
		}
	return current.pop();
};
///////////////////////////////////////////////////////////////////////////
function distanceFunc(str1, str2) {
	if (str1 === null && str2 === null) throw 'Trying to compare two null values';
	if (str1 === null || str2 === null) return 0;
	str1 = String(str1); str2 = String(str2);

	var distance = levenshtein(str1, str2);
	if (str1.length > str2.length) {
		return 1 - distance / str1.length;
	} else {
		return 1 - distance / str2.length;
	}
}
///////////////////////////////////////////////////////////////////////////
//work in progess
var selStrCache = [];
var distanceCache = [];
function dis(selStr, str, caseSensitive, matchMode, fuzzyThreshold, fullWord) {
	var o = false;
	//todo - do not strip special chars in non-fuzzy mode?
	if (caseSensitive == undefined) caseSensitive = false;
	if (matchMode == undefined) matchMode = 'all'; //or 'any' words
	if (fuzzyThreshold == undefined) fuzzyThreshold = 80; //100 = no fuzzy logic
	if (fullWord == undefined) fullWord = true; //applies to non-fuzzy mode only

	if (selStrCache[selStr] == undefined){
		var selStrOrig = selStr;
		if (!caseSensitive) selStr = selStr.toLowerCase();
		selStr = selStr.replace(/[#0123456789\!\#\&\'\,\.\-\(\)\[\]]/g," ");
		selStr = selStr.replace(/(^|\s)[a-z]{1,3}(\s|$)/g," ");
		selStr = selStr.replace(/\s+/g," ");
		selStrCache[selStrOrig] = selStr;
	} else {
		selStr = selStrCache[selStr];
	}

	if (!caseSensitive) str = str.toLowerCase();
	str = str.replace(/[#0123456789\!\#\&\'\,\.\-\(\)\[\]]/g," ");
	str = str.replace(/(^|\s)[a-z]{1,3}(\s|$)/g," ");
	str = str.replace(/\s+/g," ");

	if (o) DOpus.Output("str   : " + str);
	if (o) DOpus.Output("selStr: " + selStr);
	//return false;

	var selItemWords = new String(selStr).split(" "), selItemWords2 = [];
	var itemWords = new String(str).split(" "), itemWords2 = [];

	for(var i=0;i<itemWords.length;i++){
		if (itemWords[i].length<3) continue;
		itemWords2[itemWords2.length]=itemWords[i];
	}
	for(var i=0;i<selItemWords.length;i++){
		if (selItemWords[i].length<3) continue;
		selItemWords2[selItemWords2.length]=selItemWords[i];
	}

	if (!itemWords.length) { if (o) DOpus.Output( "No itemWords!"); return 0; }
	if (!itemWords2.length) { if (o) DOpus.Output( "No itemWords2!"); return 0; }
	var matches = 0, distance, cacheID="";

	for(var i=0;i<itemWords2.length;i++){
		var match = false;
		if (o) DOpus.Output("itemWords2[i]="+itemWords2[i]);
		//DOpus.Output("    ="+selStr.search(new RegExp('(^| |_|\.)'+itemWords2[i]+'($| |_|\.)')));
		//if (selStr.search('(^| |_|\.)'+itemWords2[i]+'($| |_|\.)')==-1)
			//continue;
		for(var ii=0;ii<selItemWords2.length;ii++){
			if (fuzzyThreshold<100){ //fuzzy mode
				cacheID = ""+selItemWords2[ii]+" # "+itemWords2[i];
				if (distanceCache[cacheID] == undefined){
					distance = this.distanceFunc( selItemWords2[ii], itemWords2[i]);
					distanceCache[cacheID] = distance;
				}
				else {
					distance = distanceCache[cacheID];
					//DOpus.Output("distanceCache USE!: " + selItemWords2[ii]+" # "+itemWords2[i]);
				}
				if ((distance*100) >= fuzzyThreshold){
					if (o) DOpus.Output("-> Fuzzy word match: " + selItemWords2[ii] + " <-> " + itemWords2[i]);
					match = true;
					break;
				}
			} else { //non-fuzzy mode
				if ((fullWord && (selItemWords2[ii] == itemWords2[i])) ||
					(!fullWord && (itemWords2[i].indexOf(selItemWords2[ii])!=-1))){
					if (o) DOpus.Output("-> Strict word match: " + selItemWords2[ii] + " <-> " + itemWords2[i]);
					match = true;
					break;
				}
			}
		}
		if (match){
			matches++;
			//DOpus.Output("MATCH: itemWords2[i]="+itemWords2[i]);
		}
		//DOpus.Output("    selStr="+selStr);

	}
	if (matchMode == 'all'){
		if (matches < selItemWords2.length) { if (o) DOpus.Output("All failed."); return false; }
		if (o) DOpus.Output("-> matched all, matches ["+matches+"], itemWords.len ["+selItemWords2.length+"]");
		return true;
	}
	if (matchMode == 'any'){
		if (matches == 0) { if (o) DOpus.Output("Any failed."); return false; }
		if (o) DOpus.Output("-> matched any, matches ["+matches+"], itemWords.len ["+selItemWords2.length+"]");
		return true;
	}

	if (o) DOpus.Output("##WEIRD matchMode ["+matchMode+"] matches ["+matches+"] selItemWords2.len ["+selItemWords2.length+"]");
	return false;

	var perc = (matches*100)/itemWords.length;
	//var perc = (itemWords.length*100)/matches;
	//DOpus.Output(selStr + " " + perc.toFixed(2));
	if (perc>15){
		return true;
	}
	return false;

	//if (matches != itemWords.length) return 0;
	return matches;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_SIMILAR(cmd, sourcetab, desttab, args, metas, jsCompare){
	Log("SelectEx_SIMILAR():","T",1);

	///////////////////////////////////////////////////////////////////////////
	var selFilesEnum = new Enumerator(sourcetab.selected_files), selItem = null;
	if (!selFilesEnum.atEnd())
		selItem = selFilesEnum.item();
	var selItems = sourcetab.selected_files;

	if (!selItem){
		Log("No item selected or passed.","W");
		//Log("-","T",-1);
		//return COMMAND_FAILURE;
	}

	if (metas){
		var selItemMetas = []
		for (var i=0;i<metas.length;i++)
			selItemMetas[i] = new String(eval("selItem"+metas[i])).toLowerCase();
	}

	cmd.ClearFiles();
	if (args["INDEST"].exists){
		var enumFiles = new Enumerator(desttab.all), selFilesCount = 0;
		cmd.SetSourceTab(desttab);
	}
	else
		var enumFiles = new Enumerator(sourcetab.all), selFilesCount = 0;

	while (!enumFiles.atEnd()) {
		var item = enumFiles.item();
		try {
			var match = true;
			if (metas) for (var i=0;i<metas.length;i++){
				if (selItemMetas[i] != new String(eval("item"+metas[i])).toLowerCase()) {
					match = false;
					break;
				}
			}
			if (jsCompare && !jsCompare(selItem, selItems, item, cmd))
				match = false;

			if (match) {
				Log("Adding:" + item.realpath, "d");
				cmd.AddFile(item);
			}
		} catch(e){
			Log("Comparing failed: " + e,"X");
			Log("-","T",-1);
			return COMMAND_FAILURE;
		}
		enumFiles.moveNext();
	}

	var cmdLine = "Select FROMSCRIPT"+
					(args["MAKEVISIBLE"].exists?' MAKEVISIBLE':'')+
					(args["DESELECTNOMATCH"].exists?' DESELECTNOMATCH':'')+
					(args["PT"].exists?' '+args["PT"].value:'') +
					"";

	Log("Deselect: " + cmd.deselect, "d");
	Log("CmdLine: " + cmdLine, "d");
	cmd.RunCommand( cmdLine );

	Log("Done.","D"); Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_ITEM(cmd, args, item){
	Log("SelectEx_ITEM():","T",1);

	if (!item) item = args["ITEM"].value;

	Log("ITEM            : " + item,"D");
	Log("MAKEVISIBLE     : " + args["MAKEVISIBLE"].exists,"D");
	Log("DESELECTNOMATCH : " + args["DESELECTNOMATCH"].exists,"D");
	Log("EXACT           : " + args["EXACT"].exists,"D");
	Log("USEPATH         : " + args["USEPATH"].exists,"D");
	Log("PT              : " + args["PT"].value,"D");

	if (!item){
		Log("No item/path given.","X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var cmdLineEnd =	(args["DESELECTNOMATCH"].exists?' DESELECTNOMATCH':'')+
						(args["MAKEVISIBLE"].exists?' MAKEVISIBLE':'')+
						(args["EXACT"].exists?' EXACT':'')+
						(args["PT"].exists?' '+args["PT"].value:'')

	var parent = item.getParentPart();
	if (!args["USEPATH"].exists){
		Log("No USEPATH", "d");
		item = item.getFilePart();
		parent = "";
	}

	/*if (!parent && args["USEPATH"].exists){
		DOpus.Output("E   No path given.");
		return COMMAND_FAILURE;
	}*/

	if (parent) {
		Log("Selecting with full path.", "d");
		//select with full path
		cmd.ClearFiles();
		Log("item            : " + item,"D");
		cmd.AddFile(item);
		var cmdline = 'SELECT FROMSCRIPT' + cmdLineEnd;
		Log("command         : " + cmdline,"D");
		cmd.RunCommand(cmdline);
	} else {
		Log("Selecting by name.", "d");
		//try to select what matches by name
		var cmdline = 'SELECT "'+item+'"' + cmdLineEnd
		Log("command         : " + cmdline,"D");
		cmd.RunCommand(cmdline);
	}

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_NTH(cmd, sourcetab, args){
	Log("SelectEx_NTH():","T",1);

	var nth = parseInt(args["NTH"].value,10), range = "";
	Log("NTH    : " + nth,"D");
	if (!nth){
		Log("No 'nth' given.","X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	cmd.ClearFiles();
	for(var i=1;i<=sourcetab.all.count;i=i+nth)
		range += i+",";

	var cmdline = 'SELECT RANGE="'+range+'"'+
					(args["DESELECTNOMATCH"].exists?' DESELECTNOMATCH':'')+
					(args["MAKEVISIBLE"].exists?' MAKEVISIBLE':'')+
					(args["EXACT"].exists?' EXACT':'')+
					(args["PT"].exists?' '+args["PT"].value:'');

	Log("command: " + cmdline,"D");
	cmd.RunCommand(cmdline);
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_FROMCLIP(cmd, sourcetab, args){
	Log("SelectEx_FROMCLIP():","T",1);

	var TM = new TextMagic();
	var selItems = TM.TextToFilePaths( DOpus.GetClip("text") );
	var result = SelectArrayItems(cmd, sourcetab, args, selItems);
	Log("-","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_FROMFILE(cmd, sourcetab, args){
	Log("SelectEx_FROMFILE():","T",1);
	var TM = new TextMagic();
	var filePath = DOpus.FSUtil.Resolve(args["FROMFILE"].value);

	Log("FROMFILE            : " + filePath,"D");

	var fm = new FSMagic();
	var file = fm.OpenFile(filePath, "r", "try");
	if (file === null){
		Log("Failure opening file ["+filePath+"]","X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var selItems = [];
	while(!file.AtEndOfStream){
		var line = file.ReadLine();
		if (line=="") continue;
		line = TM.CleanFilePath(line);
		selItems[selItems.length] = line;
	}
	file.Close();

	var result = SelectArrayItems(cmd, sourcetab, args, selItems);
	Log("-","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_FROMVAR(cmd, sourcetab, args){
	Log("SelectEx_FROMVAR():","T",1);
	var vm = new VarMagic();

	var varName = args["FROMVAR"].value;
	if (varName == "") varName = "Default";
	var varNameFull = "Script.SelectEx.StoredSelection."+varName;
	Log("FROMVAR: " + varName,"D");
	var selVar = vm.GetCreateVar(DOpus, varNameFull, null, null, false);
	if (selVar === null){
		Log("Stored selection ["+varName+"] not found.","X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}
	var selItems = vm.VectorToArray(selVar.value);
	Log("items  : " + selItems.length,"D");

	var result = SelectArrayItems(cmd, sourcetab, args, selItems);
	Log("-","T",-1);
	return result;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_TOCLIP(cmd, sourcetab, args){
	Log("SelectEx_TOCLIP():","T",1);

	Log("APPEND: " + args["APPEND"].exists,"D");
	Log("items : " + sourcetab.selected.count,"D");

	if (!sourcetab.selected.count){
		Log("No selection given, aborting.","E"); Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var eol = GetEndOfLine(args);
	var clip = "";
	if (args["APPEND"].exists){
		clipContent = DOpus.GetClip("text");
		clip=new String(clipContent);
		if (clipContent!="") clip+=eol; //"\r\n";
	}

	for (var iEnum = new Enumerator(sourcetab.selected); !iEnum.atEnd();){
		var item = iEnum.item(); iEnum.moveNext();
		clip+=item.realpath;
		if (!iEnum.atEnd()) clip+=eol; //"\r\n";
	}

	DOpus.SetClip(clip);

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_TOFILE(cmd, sourcetab, args){
	Log("SelectEx_TOFILE():","T",1);

	var filePath = DOpus.FSUtil.Resolve(args["TOFILE"].value);
	Log("TOFILE: " + filePath,"D");
	Log("APPEND: " + args["APPEND"].exists,"D");
	Log("items : " + sourcetab.selected.count,"D");

	if (!args["NOFAIL"].exists && (!sourcetab.selected.count || !filePath)){
		Log("No selection or no file given, aborting.","E"); Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var fm = new FSMagic();
	var file = fm.OpenFile(filePath, args["APPEND"].exists?"a":"w", args["ENCODING"].value, true);
	if (file === null){
		Log("Failure opening file ["+filePath+"]","X");
		Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var eol = GetEndOfLine(args);
	for (var iEnum = new Enumerator(sourcetab.selected); !iEnum.atEnd();){
		var item = iEnum.item(); iEnum.moveNext();
		var line = new String(item.realpath);
		try{
			file.Write(line+(!iEnum.atEnd()?eol:""));
		} catch(e) {
			line = line.replace(/[^ -~]/ig,''); //printable chars only
			file.Write(line+(!iEnum.atEnd()?eol:""));
		}
	}
	file.Close();

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_TOVAR(cmd, sourcetab, args){
	Log("SelectEx_TOVAR():","T",1);
	var vm = new VarMagic();

	var varName = args["TOVAR"].value;
	if (varName == "") varName = "Default";
	var varNameFull = "Script.SelectEx.StoredSelection."+varName;
	Log("TOVAR : " + varName,"D");
	Log("APPEND: " + args["APPEND"].exists,"D");
	Log("items : " + sourcetab.selected.count,"D");

	if (!args["NOFAIL"].exists && !sourcetab.selected.count){
		Log("No selection, aborting.","E"); Log("-","T",-1);
		return COMMAND_FAILURE;
	}

	var selection = vm.GetCreateVar(DOpus, varNameFull, DOpus.Create.Vector(), true, true).value;
	if (!args["APPEND"].exists)	selection.clear();
	for (var iEnum = new Enumerator(sourcetab.selected); !iEnum.atEnd();){
		var item = iEnum.item(); iEnum.moveNext();
		selection.push_back(item.realpath);
	}

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectEx_CLEARVAR(cmd, sourcetab, args){
	Log("SelectEx_CLEARVAR():","T",1);

	if (args["CLEARVAR"].value == "-"){
		Log("Clear all Stored Selections");
		DOpus.Vars.Delete("Script.SelectEx.StoredSelection.*");
	}
	
	var v, varNames = args["CLEARVAR"].value.split(';');
	
	for(v=0;v<varNames.length;v++){
		var varName = varNames[v];
		if (varName == "") varName = "Default";
		var varNameFull = "Script.SelectEx.StoredSelection."+varName;
		if (DOpus.Vars.Exists(varNameFull)){
			DOpus.Vars.Delete(varNameFull);
			Log("Clearing : " + varName,"D");
		} else {
			Log("Not found: " + varName,"D");
		}
	}

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function SelectArrayItems(cmd, sourcetab, args, selItems){
	Log("SelectArrayItems():","T",1);
	cmd.ClearFiles();
	var cmd = DOpus.Create.Command();
	cmd.SetSourceTab(sourcetab);
	cmd.deselect = false;
	cmd.ClearFiles();

	if (!selItems.length){
		Log("No usable content (no item?), aborting.","I");
		Log("-","T",-1);
		return COMMAND_FAILURE; //no valid clipboard content
	}

	var flatView		= cmd.IsSet("FLATVIEW=on");
	var selItemsBefore	= sourcetab.selected.count;
	var selItemsDone	= 0;
	var madeVisible		= null;

	Log("Params:","D");
	Log("    SKIPITEMSWITHPATH   : " + args["SKIPITEMSWITHPATH"].exists,"D");
	Log("    SKIPITEMSWITHOUTPATH: " + args["SKIPITEMSWITHOUTPATH"].exists,"D");
	Log("    USEPATH             : " + args["USEPATH"].exists,"D");
	Log("    MAKEVISIBLE         : " + args["MAKEVISIBLE"].exists,"D");
	Log("    DESELECTNOMATCH     : " + args["DESELECTNOMATCH"].exists,"D");
	Log("    EXACT               : " + args["EXACT"].exists,"D");
	Log("    GO                  : " + args["GO"].exists,"D");
	Log("    PT                  : " + args["PT"].value,"D");
	Log("    flatview            : " + flatView,"D");

	Log("Selecting items..","D");
	cmd.AddLine('//prevent no line added expection');

	if (args["DESELECTNOMATCH"].exists){
		Log("    cmd.AddLine(Select NONE);","D");
		cmd.AddLine('Select NONE');
	}

	for(var i=0;i<selItems.length;i++){
		var item = new String(selItems[i]), parent = "";
		var regexp = false;
		if (item.substring(0,6)=="REGEX="){
			item = item.substring(6);
			regexp = true;
		} else {
			Log("-itema: " + String(item), "D");
			parent = new String(item).getParentPart();
			item = new String(item).getFilePart();
			Log("-itemb: " + item, "D");
			Log("-parent0a: " + parent, "D");
		}

		if (parent && args["SKIPITEMSWITHPATH"].exists) continue;
		if (!parent && args["SKIPITEMSWITHOUTPATH"].exists) continue;
		Log("    item ["+item+"], parent ["+parent+"]","D");
		selItemsDone++;

		if (args["GO"].exists && parent){
			args["GO"].exists = false;
			if (!DOpus.FSUtil.ComparePath(parent,sourcetab.path)){
				var cmdline =	'Go "'+parent+'"'+
								(args["GOPT"].exists?' '+args["GOPT"].value:'');
				Log("    cmd.RunCommand("+cmdline+");","D");
				cmd.RunCommand(cmdline);
				sourcetab.Update();
			}
		}

		if (!args["USEPATH"].exists)
			parent = ""; //not considerung fullpaths by default
		if (parent)	parent = parent.rTrim("\\\\")+"\\";
		Log("    parent1 ["+parent+"];","D");


		if (flatView || regexp){
			if (parent && !regexp){ //fullpath works in flatview with AddFile() and SELECT FROMSCRIPT
				var file = parent.rTrim("\\\\")+"\\" + item;
				Log("    parent2 ["+parent+"];","D");
				Log("    AddFile1("+file+");","D");
				cmd.AddFile(file);
				}
			else {
				//standalone itemname cannot be selected like a fullpath in flatview
				//so add a specific SELECT command for standalone itemname(s)
				var cmdline = 'SELECT "'+item+'"'+
								(regexp?' REGEXP':'')+
								(args["MAKEVISIBLE"].exists && !madeVisible?' MAKEVISIBLE':'')+
								(args["EXACT"].exists?' EXACT':'')+
								(args["PT"].exists?' '+args["PT"].value:'')
				Log('    AddLine('+cmdline+');',"D");
				cmd.AddLine(cmdline);
				if (args["MAKEVISIBLE"].exists) madeVisible = true;
			}
		} else {
			if (parent); //fullpath does not require special handling in non-flatview
			else {
				//standalone itemname does not work in non-flatview with AddFile() and
				//SELECT FROMSCRIPT, use the current source path to build a fullpath
				parent = new String(sourcetab.path);
			}

			//todo: some weird FTP path tinkering here.. no good.
			//there is a difficulty with ftp-paths like ftp://domain//myfile.txt (with double forward slash, irregular separator)
			//this happens for root folders of user accounts like ftp://user:pwd@domain//myfile.txt and other, not sure yet
			//what a regular ftp path should looks like

			var fileParentTrimmed = parent

			//trim any slash
			fileParentTrimmed = fileParentTrimmed.rTrim("\\\\")
			fileParentTrimmed = fileParentTrimmed.rTrim("\/")

			if (fileParentTrimmed) {
				var isFTP = parent.search(/^ftp:/i) != -1;
				if(isFTP) {
					//parent ends with "//" -> "ftp://user:pwd@domain//" -> is user root folder
					var isFTPUserRoot = parent.search(/^ftp:.+\/\/$/i) != -1;
					//handle regular parent folder (without // at the end)
					var file = parent + "/" + item;
					//handle the "//" style ending (root folder of ftp, do not add another "/" to existing "//")
					if (isFTPUserRoot)
						var file = parent + item;
				} else {
					//unc or local path
					var file = fileParentTrimmed + "\\" + item;
				}
			} else {
				//no parent path present in filename
				var file = item;
			}

			Log("    parent3 ["+parent+"];","D");
			Log("    AddFile2("+file+");","D");
			cmd.AddFile(file);
		}
	}

	Log("cmd.Run()","D");
	cmd.Run(); //select standalone itemnames in flatview

	var cmdLine = 'Select FROMSCRIPT'+
						(args["EXACT"].exists?' EXACT':'')+
						(args["MAKEVISIBLE"].exists && !madeVisible?' MAKEVISIBLE':'')+
						(args["PT"].exists?' '+args["PT"].value:'');
	Log("cmd.RunCommand("+cmdLine+")","D");
	cmd.RunCommand(cmdLine);

	Log("Results:","D",1);
	sourcetab.Update();
	if ((selItemsBefore+selItemsDone) != sourcetab.selected.count){
		Log("Less/more items selected than given or some ","I");
		Log("matching items were already selected before.","I");
	}

	Log("items total         : " + selItems.length,"D");
	Log("items filtered      : " + selItemsDone,"D");
	Log("selected before     : " + selItemsBefore,"D");
	Log("selected after      : " + sourcetab.selected.count,"D",-1);

	Log("-","T",-1);
	return COMMAND_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
function GetEndOfLine(args) {
	var eol = null;
	if (eol===null) eol = args["EOL"].exists && typeof args["EOL"].value === "undefined" ? "" : null;
	if (eol===null) eol = args["EOL"].exists ? (''+args["EOL"].value).replace("\\n","\n").replace("\\r","\r") : null;
	if (eol===null) eol = "\r\n";
	return eol;
}
///////////////////////////////////////////////////////////////////////////////
function VarMagic(){
	///////////////////////////////////////////////////////////////////////////
	this.VectorToArray = function(vector){
		var a = new Array();
		for(var v=0;v<vector.length;v++)
			a[a.length] = vector(v);
		return a;
	}
	///////////////////////////////////////////////////////////////////////////
	this.GetCreateVar = function(origin, name, iniValue, persist, create){
		Log("GetCreateVar():","T",1);
		persist = persist || false;
		create = create || false;
		Log("name     : " + name,"D");
		Log("ini-value: " + iniValue,"D");
		Log("persist  : " + persist,"D");
		Log("create   : " + persist,"D");
		if (!origin.Vars.Exists(name)){
			if (create) origin.Vars.Set( name, iniValue);
			else{
				Log("<- null","T",-1);
				return null;
			}
		}
		var doVar = origin.Vars(name);
		if (persist) doVar.persist = true; else doVar.persist = false;
		Log("-","T",-1);
		return doVar;
	}
}
///////////////////////////////////////////////////////////////////////////////
function TextMagic(){
	this.version = 0.2;
	///////////////////////////////////////////////////////////////////////////
	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<filePaths.length;i++){
			if (DOpus.FSUtil.Exists(filePaths[i]))
				items[items.length] = DOpus.FSUtil.GetItem(filePaths[i]);
		}
		return items;
	}
	///////////////////////////////////////////////////////////////////////////
	this.GetLineEndingOfText = function(text){
		var lineEndings = ["\r\n","\n\r","\n","\r"];
		var lineEnding = "";
		for(var l=0;l<lineEndings.length;l++){
			if (text.indexOf(lineEndings[l])!=-1){
				lineEnding = lineEndings[l];
				break;
			}
		}
		return lineEnding;
	}
	///////////////////////////////////////////////////////////////////////////
	this.CleanFilePath = function( pathOrName){
		pathOrName = pathOrName.trim();
		pathOrName = pathOrName.trim('"');
		pathOrName = pathOrName.replace(/[\?]/g,"");
		return pathOrName;
	}
	///////////////////////////////////////////////////////////////////////////
	this.CleanFilePaths = function( namesUnclean ){
		var itemsCleaned = new Array();
		for(var c=0;c<namesUnclean.length;c++){
			var newName = this.CleanFilePath(namesUnclean[c]);
			if (newName!="")
				itemsCleaned[itemsCleaned.length] = newName;
		}
		return itemsCleaned;
	}
}
///////////////////////////////////////////////////////////////////////////////
function Log(text, lvl, ind){ //XLog v0.41
    var u,lvs={o:0,x:1,e:2,w:3,i:4,n:5,t:6,d:7,a:8},i=["","X","E","W","I","","","",""];
	if (typeof XLog==(u="undefined")){var v=DOpus.Vars; if (v.Exists("XLog") && typeof XLogForce==u) {XLog=v.Get("XLog"); }}
	if (typeof XLog==u){var c=Script.Config; if(typeof c!="undefined" && typeof c.XLog!=u){ XLog=c.XLog; }}
	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).substring(0,1)]; XLog={paused:false,I:0,L:L};}
	lvl=(lvl==undefined?5:lvs[lvl.substring(0,1).toLowerCase()]);
	if (!(lvl && XLog.L && !XLog.paused && (lvl<=XLog.L))){ return;} var pad = (XLog.I==0?"":new Array(XLog.I+1).join("    "));
	if (i[lvl])pad = i[lvl]+(!pad?"   ":pad.substring(1)); if (text!="-"){ var d=DOpus; if (d.Version.AtLeast("11.13.1"))
	d.Output(pad+text,((lvl==1||lvl==2)?1:0)); else d.Output(pad+text);}
	ind=(ind!==undefined?ind:0);XLog.I+=ind;if(XLog.I<0)throw new Error("XLog indent went sub-zero.");
}
///////////////////////////////////////////////////////////////////////////////
function FSMagic(){
	this.version = 0.7;
	//v0.7 - add support for / and \ in getFilePart() getParentPart()
	//v0.6 - fix for opening in "r" mode never failing for nonexisting files (files blindly created)
	//       renamed encoding "def" to "default" in encoding map

	this.fso = new ActiveXObject("Scripting.FileSystemObject");
	this.mappingsCached	= [];
	///////////////////////////////////////////////////////////////////////////
	this.GetTmpFileName = function(prefix, extension) {
		var tFolder = this.fso.GetSpecialFolder(2); //2 = temp folder
		var tFile = this.fso.GetTempName();
		if (prefix!=undefined) tFile=prefix+tFile;
		if (extension!=undefined) tFile+=extension;
		return {
			path	: tFolder.Path,
			name	: tFile,
			fullname: tFolder.Path+'\\'+tFile
		};
	}
	///////////////////////////////////////////////////////////////////////////
	this.OpenFile = function(filePath, mode, encoding, create){
		mode = mode || "r"; encoding = encoding || "unicode";
		create = (create === void 0 ? false : create);
		var testEncoding = (encoding=="try"?true:false);
		var encodings = {'ascii':0,'unicode':-1,'default':-2}, modes = {1:1,r:1,2:2,w:2,8:8,a:8};
		try{
			if (mode=="w") create = true;
			if (mode=="w" && this.fso.FileExists(filePath)) this.fso.DeleteFile(filePath);
			if (mode=="r" && testEncoding) encoding="ascii";
			Log("Mode    : " + modes[mode] + " - " + mode, "d");
			Log("Encoding: " + encodings[encoding] + " - " + encoding, "d");
			Log("Create  : " + create, "d");
			Log("Path    : " + filePath, "d");
			var file = this.fso.OpenTextFile(filePath, modes[mode], create, encodings[encoding]);
			if (testEncoding && mode!="w" && !file.AtEndOfStream){
				var line = file.ReadLine();
				if (line.indexOf("ï»¿")==0) {//utf8 bom
					Log("Unsupported UTF8 file detected, convert to UTF16-LE or UNICODE first (with notepad maybe).", "X");
					file.Close(); return null;
				}
				if (line.indexOf("þÿ")==0) {//utf16 be bom
					Log("Unsupported UTF16-BE file detected, convert to UTF16-LE or UNICODE first (with notepad maybe).", "X");
					file.Close(); return null;
				}
				if (line.indexOf("ÿþ") == 0) encoding="unicode"; //utf16 le bom
				file.Close(); //close and reopen file, since we cannot rewind the textstream in case
				//it is ascii and for unicode we need to reopen it anyway
				var file = this.fso.OpenTextFile(filePath, modes[mode], create, encodings[encoding]);
			}
			return file;
		} catch(e){
			Log("Error opening file ["+filePath+"]: " + e, "D");
			return null;
		}
	}
	///////////////////////////////////////////////////////////////////////////
	this.CreatePath = function(fullPath){
		//version = 0.5.1;
		fullPath = new String(fullPath);
		if (this.fso.FolderExists(fullPath)) return true;
		var drive = fullPath.substring(0,3);
		var dirs = fullPath.split("\\");
		var path = ""
		for(var i=1;i<dirs.length;i++){
			var dir = dirs[i];
			if (path!="") path += "\\"; else path = drive;
			path += dir;
			if (this.fso.FolderExists(path)) continue;
			try{
				this.fso.CreateFolder(path);
			}
			catch (e){
				return false;
			}
		}
		return true;
	}
	///////////////////////////////////////////////////////////////////////
	this.GetLetter = function(path){
		var letterMatch = String(path).match(/^([A-Z]):.*/);
		if (!letterMatch)
			return "";
		return letterMatch[1];
	}
	///////////////////////////////////////////////////////////////////////
	this.GetNetworkPathForDriveLetter = function(letter){
		if (letter.length==1) letter+=":";
		letter=letter.toUpperCase();
		if (this.mappingsCached[letter])
			return this.mappingsCached[letter];
		var objNetwork = new ActiveXObject("WScript.Network");
		var colDrives = objNetwork.EnumNetworkDrives();
		for(var i=0;i<colDrives.length;i+=2){
			if (colDrives.Item(i) == letter){
				var unc = colDrives.item(i+1)
				this.mappingsCached[letter] = unc;
				return unc;
			}
		}
		return "";
	}
	///////////////////////////////////////////////////////////////////////
	this.TranslateToUNCPath = function(path){
		var letter = this.GetLetter(path);
		if (!letter)
			return "";
		var mappedPath = this.GetNetworkPathForDriveLetter(letter);
		var nonRoot = String(path).substring(3);
		if (mappedPath)
			uncPath = mappedPath;
		else {
			var ip = NetMagic.GetLocalIPAddress();
			var uncPath = "\\\\"+ip+"\\"+letter.toLowerCase()+"$";
		}
		return uncPath+(nonRoot?"\\"+nonRoot:"");
	}
	///////////////////////////////////////////////////////////////////////////
	String.prototype.getFilePart = function (){
		if (this.indexOf("\\")!=-1)
			return this.split("\\").slice(-1)[0];
		return this.split("\/").slice(-1)[0];
	}
	///////////////////////////////////////////////////////////////////////////
	String.prototype.getParentPart = function (){
		if (this.indexOf("\\")!=-1) {
			var arr = this.split("\\");
			arr.pop(); //remove last element
			return arr.join("\\");
		} else {
			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(".");
		arr.pop();
		return arr.join(".");
	}
}
///////////////////////////////////////////////////////////////////////////////
function ArgsMagic(data, jsonData, template){
	this.version = 0.42;
	///////////////////////////////////////////////////////////////////////////
	function Arg(name, type, exists, value, subOpsTemplate){
		this.name = name; this.exists = exists; this.type = type;
		this.value = value; this.options = []; if (!this.exists) return;
		switch (type){ case "switch":; case "numeric":; case "multiple": return;}
		if (this.type=="optional" && this.value===true) this.value="";
		var op,ops=this.value!=""?new String(this.value).split(","):null, match;
		if (ops && ops.length) for(op in ops) {
			var opName = ops[op].replace('"','');
			var opValue = true, parts = opName.split(":");
			opName = parts[0].trim("<").trim(">");
			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<maxLen) pad+=" ";
		propName = (upperCaseProps?prop.toUpperCase():prop);
		if (!o.hasOwnProperty(prop))
			continue;
		switch(typeof o[prop]) {
			case "function":
				DOpus.Output(level+propName+pad+": function(){}..");
			break;
			case "object":
				DOpus.Output(level+propName+pad+": "+o[prop]);
				DumpObject(o[prop], propName, upperCaseProps, false, level+"    ");
			break;
			default:
				DOpus.Output(level+propName+pad+": "+o[prop]);
		}
	}
}
///////////////////////////////////////////////////////////////////////////////
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 = "197b95e36dd17f902cefe18f362b464c"; DATE = "2021.03.07 - 01:30:05"
