﻿// CommandAsZip
// (c) 2024-2025 Cris
var script_name = 'ZipIt';
var script_version = '2.0.0';

function OnInit(initData) {
	initData.name = script_name;
	initData.version = script_version;
	initData.copyright = '(c) 2024-2025 Cris';
	initData.url = 'https://resource.dopus.com/t/zipit-open-selected-files-as-zips/53932';
	initData.desc = 'Open selected files as if they were zips';
	initData.default_enable = true;
	initData.min_version = '13.16.1';
	initData.config = DOpus.Create.OrderedMap();
	initData.config_desc = DOpus.Create.OrderedMap();
	initData.config_groups = DOpus.Create.OrderedMap();
	AddConfig('log level', DOpus.NewVector(2, 'debug', 'standard', 'warning', 'off'), DOpus.strings.Get('config_log_level'), 'General');
	AddConfig('relative temp path', '', DOpus.strings.Get('config_relative_path'), 'General');
	AddConfig('valid exts', DOpus.NewVector(), DOpus.strings.Get('config_valid_exts'), 'General');

	function AddConfig(name, value, desc, group) {
		initData.config[name] = value;
		initData.config_desc(name) = desc;
		initData.config_groups(name) = group;
	}
}

// Called to add commands to Opus
function OnAddCommands(addCmdData) {
	var cmd = addCmdData.AddCommand();
	cmd.name = script_name;
	cmd.method = 'OnAsZip';
	cmd.desc = 'Seemingly opens selected files as if they were zips';
	cmd.label = script_name;
	cmd.template = 'FILES/M,CONFIRM/S,DUAL/S';
	cmd.hide = false;
	cmd.icon = 'script';
}

// Implement the AsZip command
function OnAsZip(SCData) {
	DOpus.ClearOutput();
	Log(2, '====== ' + script_name + ' v' + script_version + ' ======');
	Log(1, 'cmdline : ' + SCData.cmdline);
	var tab = SCData.func.sourcetab;
	var lister = tab.lister;
	var FSU = DOpus.FSUtil();
	var strings = DOpus.strings;
	var cmd = SCData.func.command;
	cmd.deselect = false;
	var items = DOpus.NewVector();
	if (SCData.func.args.got_arg.FILES) items.assign(SCData.func.args.FILES);
	if (items.empty && cmd.filecount) items.assign(cmd.files);

	if (items.empty) {
		Log(4, alert(tab, strings.Get('msg_no_files')));
		return;
	}
	var accept = !SCData.func.args.got_arg.CONFIRM; //cache request answer, to only ask once
	var win_temp_dir = FSU.Resolve('/temp'); //path to %temp% folder
	//relative path to use for other drives. e.g. temp_rel_path = my_temp_folder\\temp_hardlinks, then path will be x:\\my_temp_folder\\temp_hardlinks for files in x:\\
	var temp_rel_path = Script.config['relative temp path'];
	if (temp_rel_path.trim() === '') temp_rel_path = '.DO_aszip_temp';
	var valid_exts = DOpus.Create().StringSetI(Script.config['valid exts']);
	var all_exts = valid_exts.empty;
	var links_map = DOpus.NewMap();
	var temp_items = DOpus.NewVector();
	var temp_path, drive_item, item, link_type;
	var dual = SCData.func.args.got_arg.DUAL;
	cmd.ClearFiles();
	main: for (var i = 0; i < items.count; i++) {
		item = items(i);
		sw = false;
		if (!FSU.Exists(item)) continue;
		if (DOpus.TypeOf(item) !== 'object.Item') item = FSU.GetItem(item);

		if (item.is_dir || item.InGroup('Archives')) { //open as usual since is a dir/archive
			Log(2, 'Opening "' + item + '" as a regular item...');
			cmd.RunCommand('Go "' + item + '" NEWTAB=tofront,findexisting');
			continue;
		}
		drive_item = item.realpath.GetDrive();
		if (!drive_item) {
			Log(4, '"' + item + '" is not in a supported path type!');
			continue;
		}
		if ((!all_exts && (!item.ext || !valid_exts.Exists(item.ext.toLowerCase()))) || item.is_reparse || item.is_symlink) {
			Log(4, '"' + item + '" is not considered valid for this command!');
			continue;
		}
		try {
			if (!accept) accept = cmd.Dlg().Request(strings.Get('msg_ask_confirm'), strings.Get('msg_ask_confirm_yes') + '|' + strings.Get('msg_ask_confirm_no'), script_name, lister) !== 0; //ask for permission but just once
			if (!accept) break main;
			Log(1, 'Setting a temp path for "' + item + '"...');
			if (!links_map.Exists(drive_item))
				updateLinksTypeMap(drive_item); //drive doesn't exists in map yet, so we must first get their info

			link_type = links_map(drive_item);
			if (!link_type) {
				Log(4, strings.Get('msg_no_linktype').replace('%1', item));
				continue;
			}
			temp_path = FSU.NewPath();
			if (item.realpath.drive === win_temp_dir.drive || link_type === 'softlink') //item is in the same drive as %temp% or we're going to create a softlink to %TEMP%
				temp_path.Set(win_temp_dir); //change path to %TEMP%
			else {
				temp_path.Set(drive_item.def_value);
				try {
					temp_path.Add(temp_rel_path); //append temp_rel_path value
				}
				catch (err) {
					Log(3, '=> Unable to add "' + temp_rel_path + '" to Path object. Using ".DO_aszip_temp" as fallback...');
					temp_path.Add('.DO_aszip_temp');
				}

			}
			temp_path.Add(item.name + '.zip'); //append item name with zip ext 
			Log(1, '=> Temp path : ' + temp_path);
			var counter = 1;
			while (FSU.Exists(temp_path) && counter <= 10) {
				temp_path.Set(temp_path.pathpart + '\\' + item.name + '_' + counter + '.zip');
				counter++;
			}
			if (counter > 10) {
				Log(3, '=> Unable to set temp path for "' + item + '"');
				continue;
			}

			Log(2, 'Creating a ' + link_type + ' for "' + item + '" in "' + temp_path + '"...');
			cmdline = 'Copy "' + item + '" MAKELINK=' + link_type + ' AS "' + temp_path.filepart + '" TO "' + temp_path.pathpart + '" CREATEFOLDER="' + temp_path.pathpart + '"';
			Log(1, '=> cmdline : ' + cmdline);
			cmd.RunCommand(cmdline);
			if (!cmd.results.result) {
				Log(3, '=> Unable to create a ' + link_type + ' in ' + temp_path);
				continue;
			}

			//command run succesfully
			Log(2, 'Opening : "' + temp_path + '"...');
			cmdline = 'Go "' + temp_path + '" NEWTAB=tofront,findexisting' + (dual ? ' OPENINDUAL' : '');
			Log(1, '=> cmdline : ' + cmdline);
			cmd.RunCommand(cmdline); //open item link as zip
			if (cmd.results.result === 0 || cmd.results.newtabs.count == 0) //if not successful or no new tab was created
				Log(3, '=> Unable to open the zip : ' + temp_path);
			else {
				Script.Vars.Set(cmd.results.newtabs(0).def_value, temp_path.def_value); //store the new tab ID plus the hardlink zip fullpath
				temp_items.push_back(temp_path.def_value);
			}
		}
		catch (err) {
			Log(3, '=> Unable to process "' + item + '" : ' + err.description);
			continue;
		}

	}
	if (!temp_items.empty) {
		if (Script.Vars.Exists('temp_files')) temp_items.append(Script.Vars.Get('temp_files'));
		temp_items.unique();
		Log(1, 'items saved:' + temp_items.count);
		Script.Vars.Set('temp_files', temp_items);
		Script.Vars('temp_files').persist = true;
	}
	temp_items = null;
	items = null;
	links_map = null;
	cmd = null;
	win_temp_dir = null;
	item = null;
	temp_path = null;
	FSU = null;
	tab = null;
	lister = null;
	Log(1, '====== COMMAND FINISHED ======');
	return;

	function updateLinksTypeMap(drive) {

		//skip remote type drives
		//if drive is NTFS formatted, create a hardlink, otherwise a softlink to a temp folder
		links_map.Set(drive.def_value, drive.type === 'remote' ? '' :
			drive.filesys !== 'NTFS' ? 'softlink' : 'hardlink');
		return;
	}
}

// Called when a tab is closed
function OnCloseTab(closeTabData) {
	var tab = String(closeTabData.tab);
	if (!Script.Vars.Exists(tab)) return;
	var item = Script.Vars.Get(tab);
	var cmd = DOpus.Create().Command();
	DeleteItem(item, cmd);
	cmd = null;
	item = null;
	Script.Vars.Delete(tab);
	tab = null;
}

// Called when Directory Opus starts up
function OnStartup(startupData) {
	DeleteTempItems();
}

// Notifies a script it is being deleted through the Scripts management dialog
function OnDeleteScript(deleteScriptData) {
	DeleteTempItems();
	Script.vars.Delete('*');
}

function DeleteTempItems() {
	Log(1, 'Deleting all saved items links...');
	if (!Script.vars.Exists('temp_files')) return;
	var temp_files = Script.vars.Get('temp_files');
	Log(1, 'items saved:' + temp_files.count);
	var cmd = DOpus.NewCommand();
	for (var i = temp_files.count - 1; i >= 0; i--) {
		DeleteItem(temp_files(i), cmd);
	}
	cmd = null;
	temp_files = null;
	Script.vars.Delete('temp_files');
}

function DeleteItem(item, cmd) {
	Log(1, '=> Deleting link from "' + item + '"...');
	if (!cmd.RunCommand('Delete "' + item + '" QUIET NORECYCLE'))
		Log(3, '=>=> Unable to delete it!');
	return;
}

function alert(parent, message, level, choose) {
	var dlg = DOpus.Dlg();
	if (parent) {
		dlg.window = parent;
		dlg.disable_window = parent;
	}
	dlg.message = message;
	dlg.buttons = choose ? choose : '&OK';
	dlg.title = script_name + ' v' + script_version;
	if (level === 0) dlg.icon = 'info';
	else if (level === 1) dlg.icon = 'question';
	else if (level === 2) dlg.icon = 'warning';
	else dlg.icon = 'error';
	dlg.Show();
	return choose ? dlg.result : message;
}

function Log(level, text) {
	if (level === 4 || Script.config['log level'] < level) {
		if (level == 1) DOpus.Output('<#%vs_dragdrop_normal_action>DEBUG   => ' + text + '</#>');
		else if (level == 2) DOpus.Output('INFO    => ' + text);
		else if (level === 3) DOpus.Output('<#%vs_dragdrop_warning_action>WARNING => ' + text + '</#>');
		else DOpus.Output('ERROR   => ' + text, true);
	}
}
String.prototype.trim = function() {
	return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
==SCRIPT RESOURCES
<resources>
	<resource type="strings">
		<strings lang="english">
			<string id="config_delete_links">Delete saved file links when Opus starts or when this script is uninstalled.</string>
			<string id="config_log_level">Logging level to be displayed. OFF to show only errors. 
DEBUG to show all messages. 
STANDARD to show only the most relevant information.
WARNING to show messages that needs your attention.</string>
			<string id="config_relative_path">Relative path to the folder where temporary links will be stored. Note that this value is appended to each root disk.   
E.g. config_relative_path = my_temp_folder\\temp_hardlinks will result in the temporary path being x:\\my_temp_folder\\temp_hardlinks for files on x:\\ used with this command.</string>
			<string id="config_valid_exts">List of extensions that will be considered by the command. Leave empty to use all extensions.</string>
			<string id="msg_ask_confirm">Do you want to open these files?</string>
			<string id="msg_ask_confirm_no">No</string>
			<string id="msg_ask_confirm_yes">Yes</string>
			<string id="msg_no_files">No files available for this command!</string>
			<string id="msg_no_linktype">Unable to find a valid link type for &quot;%1&quot;. 
Maybe is in a remote disk!</string>
		</strings>
		<strings lang="esm">
			<string id="config_delete_links">Eliminar enlaces a archivos cuando Opus inicie o al desinstalar este script.</string>
			<string id="config_log_level">Nivel de registro a mostrar. OFF para mostrar solo errores. 
DEBUG para mostrar todos los mensajes. 
STANDARD para mostrar solo la información más relevante. 
WARNING para mostrar mensajes que requieren atención.</string>
			<string id="config_relative_path">Ruta relativa a la carpeta donde se guardaran los enlaces temporales. 
Ej. config_relative_path = my_temp_folder\\temp_hardlinks, la ruta temporal será x:\\my_temp_folder\\temp_hardlinks para archivos en x:\\ usados con este comando.</string>
			<string id="config_valid_exts">Lista de extensiones que serán tomadas en cuenta por el comando. Dejar vacío para usar todas las extensiones.</string>
			<string id="msg_ask_confirm">¿Desea abrir estos archivos?</string>
			<string id="msg_ask_confirm_no">No</string>
			<string id="msg_ask_confirm_yes">Si</string>
			<string id="msg_no_files">¡No hay archivos disponibles para este comando!</string>
			<string id="msg_no_linktype">No se pudo establecer un tipo de enlace para &quot;%1&quot;.
Puede que esté en una ruta remota.</string>
		</strings>
	</resource>
</resources>
