Find-As-You-Type (FAYT) scripts

Example 2 - Custom search via FAYT

A very different example:

(Download and copy to /scripts, or use the new Settings > Install Script command.)

  • This adds a FAYT Quick Key which runs a particular Find Files Preset (g_preset, defined at the top of the script), using whatever you type as the filename pattern.

  • The intent is to quickly search a pre-defined list of folders, regardless of the current location and without having to open the Find Files panel.

  • The search does not start until you push return.

  • Searches always use "Any Word" mode by default (set by g_anyWord near the top of the script), meaning the words you type can match in any order, and don't all need to match as long as at least one does. (You can prefix a word with + to say it MUST match, or with - to say it must NOT match.)

  • If both g_anyWord and g_needAllWords are set to true, each word you type is automatically prefixed with a + character, so that all words must be present but can be in any order. If g_anyWord is false, g_needAllWords is not used.

  • The Find Preset is named MySearchPreset by default, but you can edit g_preset at the top of the script to change the name. You need to create a preset with that name, and use it to define the folders you want to search. You can do that from the Tools > Find Files.

    The Find Panel is also where you can edit the preset, if you need to change the list of folders or any other options, and then save out the preset again.

  • A menu-button can also be added to your toolbars which lists the current and recent
    searches:

    The menu part shows your previous searches, so you can re-run them, and a Clear option to clear the list. (This will hide the button entirely, until the history is non-empty again.)

    The history is not saved to disk by default, but you can change that at the top of the script, as well as the maximum number of items it will remember.

    Make a button which runs FAYTSearchExample_History LIST to get the list. Remember that it will not appear until you've done a search and have something for it to show.

    You can also use FAYTSearchExample_History CLEAR if you want a standalone button that clears the list.

    The dynamic history list is generated using functionality added in Directory Opus 13.15.2. More detail can be found in the 13.15.2 release notes.

Here is the script code in text format so it's easier to see how it works. This is the same as the download at the top of the example.

// This adds a FAYT Quick Key which runs a particular Find Files Preset (g_preset, below),
// adding whatever you type as the filename pattern.
// The intention is to be able to quickly search a pre-defined list of folders,
// regardless of the current location and without having to open the Find Files panel.

// The search does not start until you push return.

// A menu-button can also be added to your toolbars which lists the current and recent
// searches, and lets you clear them. The button part shows the last search and the menu
// shows previous searches so you can re-run them.
// Make a button which runs "FAYTSearchExample_History LIST" to get that.

var g_preset = "MySearchPreset";
var g_nameInternal = "FAYTSearchExample";
var g_defKey = "!";
var g_defBackColor = "#ffc6c6";
var g_defTextColor = "#000000";
var g_nameHistoryVar = g_nameInternal + "_History";
var g_nameHistoryCmd = g_nameInternal + "_History";
var g_nameHistoryLab = g_nameInternal + " History";
var g_nameHistoryClearLab = "&Clear";
var g_historyPersist = false; // Set true to save history to disk across restarts.
var g_historyLimit = 10;

// Searches always use "Any Word" mode by default, meaning the words you type can match in any order, and don't all need to match as long as
// at least one does. You can prefix a word with + to say it MUST match, or with - to say it must NOT match.
var g_anyWord = true;

// If both g_anyWord and g_needAllWords are true, each word you type is automatically prefixed with a + character. So all words must be present
// but can be in any order. If g_anyWord is false, g_needAllWords is not used.
var g_needAllWords = true;

function OnInit(initData)
{
	initData.name = "FAYT Search " + g_preset;
	initData.version = "1.1";
	initData.copyright = "(c) 2021-2025 Leo Davidson";
	initData.url = "https://resource.dopus.com/t/find-as-you-type-fayt-scripts/44736/1";
	initData.desc = "";
	initData.default_enable = true;
	initData.min_version = "13.15.2";
	initData.group = "FAYT";
}

function OnDeleteScript(deleteScriptData)
{
	// Script is being deleted through the Scripts management dialog.
	// Clear the history. This is mainly in case it has been configured to persist to disk.
	ClearHistory();
}

function OnAddCommands(addCmdData)
{
	// Search command which is run when typing into the FAYT field with our prefix.
	var cmd = addCmdData.AddCommand();
	cmd.name = g_nameInternal;
	cmd.method = "OnFAYTSearch";
	cmd.desc = "";
	cmd.label = g_nameInternal;
	cmd.icon = "script";
	cmd.template = "";
	cmd.hide = true; // Hide from button editor menus

	var fayt = cmd.fayt;
	fayt.enable = true;
	fayt.key = g_defKey;
	fayt.backcolor = g_defBackColor;
	fayt.textcolor = g_defTextColor;
	fayt.label = "Search " + g_preset;
//	fayt.flags - optional Map of flags (flag value -> label) - need to use DOpus.Create.Map
	fayt.realtime = false; // Call after return, not on every keypresss

	// History command which generates a button-menu of previous searches.
	cmd = addCmdData.AddCommand();
	cmd.name = g_nameHistoryCmd;
	cmd.method = "OnFAYTSearchHistory";
	cmd.desc = "Used to show or clear history of searches made via the FAYT Search Preset script.";
	cmd.label = g_nameHistoryLab;
	cmd.icon = "findpresets";
	cmd.template = "CLEAR/S,LIST/S,RUN/K";
	cmd.dynamic_args = "LIST";
}

function OnFAYTSearch(scriptFAYTData)
{
	// Called when the user types into the FAYT field with our prefix.
	if (scriptFAYTData.fayt != g_nameInternal)
	{
		DOpus.Output('Unexpected FAYT: "' + scriptFAYTData.fayt + '"');
		return;
	}

	if (scriptFAYTData.key != "return")
	{
		DOpus.Output('Unexpected FAYT: "' + scriptFAYTData.fayt + '" called unexpectedly');
		return;
	}

	var tab = scriptFAYTData.tab;
	var strQuery = scriptFAYTData.cmdline;

	if (strQuery == "" || strQuery == scriptFAYTData.quickKey)
	{
		return;
	}

	RunQuery(tab, strQuery);
}

function RunQuery(tab, strQuery)
{
	strQuery = strQuery.replace(/^\s*(.+?)\s*$/,"$1"); // Trim spaces from start and end.
	strQuery = strQuery.replace(/"/g,""); // Remove any quote characters in the string.

	if (strQuery == "")
	{
		return;
	}

	AddHistory(strQuery);

	var cmdLine = 'Find PRESET="' + g_preset + '" COLLNAME="Find Results" CLEAR';
	if (g_anyWord)
	{
		cmdLine += ' ANYWORD';
		if (g_needAllWords)
		{
			// Prefix each word with "+".
			strQuery = strQuery.split(" ");
			for(var i = 0; i < strQuery.length; ++i)
			{
				if (strQuery[i] != "" && strQuery[i][0] != "+" && strQuery[i][0] != "-")
				{
					strQuery[i] = '+' + strQuery[i];
				}
			}
			strQuery = strQuery.join(" ");
		}
	}
	
	cmdLine += ' NAME="' + strQuery + '"';
	var cmd = DOpus.Create.Command();
	cmd.SetSourceTab(tab);
	cmd.RunCommand(cmdLine);
//	DOpus.Output(cmdLine);
}

function OnFAYTSearchHistory(scriptCommandData)
{
	var args = scriptCommandData.func.args;

	if (args.got_arg.CLEAR)
	{
		ClearHistory();
	}
	else if (args.got_arg.RUN)
	{
		RunQuery(scriptCommandData.func.sourcetab, args.RUN);
	}
}

function OnAddButtons(addButtonsData)
{
	// Make sure we're being called because of the LIST argument, otherwise bail.
	if (!addButtonsData.args.got_arg.LIST)
	{
		return false;
	}

	var vecHistory = GetHistory();

	// If the history is empty, emit nothing and hide the original button.
	// (All we could add here is a clear button, and it's already cleared.)
	if (vecHistory.empty)
	{
		return true;
	}

	// Make a menu-button with the most recent search on the button part.
	var menuButton = addButtonsData.buttons.AddMenuButton();
	menuButton.label = DoubleAmpersands(vecHistory(0));
	menuButton.func = g_nameHistoryCmd + ' RUN="' + vecHistory(0) + '"';
	menuButton.image = "#find";
	menuButton.notablabel = true;
	// Inherit our top-level label/image from the button that generates us.
//	menuButton.showLabel = "right";
//	menuButton.showImage = true;

	// In the menu part of the menu-button, add the rest of the searches.
	var menu = menuButton.children;
	for (var i = 1; i < vecHistory.count; ++i)
	{
		var searchButton = menu.AddButton();
		searchButton.label = DoubleAmpersands(vecHistory(i));
		searchButton.func = g_nameHistoryCmd + ' RUN="' + vecHistory(i) + '"';
		searchButton.image = "#find";
		searchButton.notablabel = true;
	//	searchButton.showLabel = "right"; // Not needed as "right" is the default for labels within sub-menus.
		searchButton.showImage = true;
		if (i == vecHistory.count - 1)
		{
			searchButton.separator = true; // Separator after this button.
		}
	}
	// Still in the menu part of the menu-button, add a Clear button.
	var clearButton = menu.AddButton();
	clearButton.label = g_nameHistoryClearLab; // Don't DoubleAmpersands. The label uses one as an accelerator.
	clearButton.func = g_nameHistoryCmd + ' CLEAR';
	clearButton.image = "#clearfilters";
	clearButton.notablabel = false; // Allow tabs in label, although we don't use them normally.
//	clearButton.showLabel = "right"; // Not needed as "right" is the default for labels within sub-menus.
	clearButton.showImage = true;
	
	return true;
}

function DoubleAmpersands(str)
{
	return str.replace(/&/g,"&&");
}

// Helpers for managing the search history.

function ClearHistory()
{
	Script.vars.Delete(g_nameHistoryVar);
	Script.UpdateButtons(true); // Update our button-menu that shows the search history.
}

function GetHistory()
{
	var v = Script.vars;
	if (!v.Exists(g_nameHistoryVar))
	{
		return DOpus.Create.Vector(); // Empty.
	}
	return v.Get(g_nameHistoryVar);
}

function SetHistory(vecHistory)
{
	var v = Script.vars;
	v.Set(g_nameHistoryVar, vecHistory);
	v(g_nameHistoryVar).persist = g_historyPersist; // Optionally save to disk.
	Script.UpdateButtons(true); // Update our button-menu that shows the search history.
}

function AddHistory(strQuery)
{
	if (g_historyLimit < 1)
	{
		ClearHistory();
		return;
	}

	if (!strQuery || strQuery == "")
	{
		return;
	}

	var strQueryLower = strQuery.toLowerCase();

	var vecHistoryOld = GetHistory();
	var vecHistoryNew = DOpus.Create.Vector();

	vecHistoryNew.push_back(strQuery);

	for (var i = 0; i < vecHistoryOld.count; ++i)
	{
		if (vecHistoryNew.count >= g_historyLimit)
		{
			break;
		}
	
		var strHistItem = vecHistoryOld(i);
		if (strQueryLower != strHistItem.toLowerCase())
		{
			vecHistoryNew.push_back(strHistItem);
		}
	}

	SetHistory(vecHistoryNew);
}
1 Like