hammerspoon/extensions/application/application.lua

474 lines
24 KiB
Lua

--- === hs.application ===
---
--- Manipulate running applications
local application = require("hs.libapplication")
application.watcher = require("hs.libapplicationwatcher")
local timer = require "hs.timer"
local settings = require "hs.settings"
local USERDATA_TAG = "hs.application"
local objectMT = hs.getObjectMetatable(USERDATA_TAG)
local alternateNameMap = {}
local spotlightEnabled = settings.get("HSenableSpotlightForNameSearches")
-- internal search tool for alternate names
local realNameFor = function(value, exact)
if type(value) ~= "string" then
error('hint must be a string', 2)
end
if not exact then
local results = {}
for k, v in pairs(alternateNameMap) do
if k:lower():find(value:lower()) then
-- I can foresee someday wanting to know how often a match was found, so make it a
-- number rather than a boolean so I can cut & paste this
results[v] = (results[v] or 0) + 1
end
end
local returnedResults = {}
for k,_ in pairs(results) do
table.insert(returnedResults, k:match("^(.*)%.app$") or k)
end
return table.unpack(returnedResults)
else
local realName = alternateNameMap[value]
-- hs.application functions/methods do not like the .app at the end of application
-- bundles, so remove it.
return realName and realName:match("^(.*)%.app$") or realName
end
end
local type,pairs,ipairs=type,pairs,ipairs
local tunpack,tpack,tsort=table.unpack,table.pack,table.sort
--- hs.application:visibleWindows() -> win[]
--- Method
--- Returns only the app's windows that are visible.
---
--- Parameters:
--- * None
---
--- Returns:
--- * A table containing zero or more hs.window objects
function objectMT.visibleWindows(self)
local r={}
if self:isHidden() then return r -- do not check :isHidden for every window
else for _,w in ipairs(self:allWindows()) do if not w:isMinimized() then r[#r+1]=w end end end
return r
end
--- hs.application:activate([allWindows]) -> bool
--- Method
--- Tries to activate the app (make its key window focused) and returns whether it succeeded; if allWindows is true, all windows of the application are brought forward as well.
---
--- Parameters:
--- * allWindows - If true, all windows of the application will be brought to the front. Otherwise, only the application's key window will. Defaults to false.
---
--- Returns:
--- * A boolean value indicating whether or not the application could be activated
function objectMT.activate(self, allWindows)
allWindows=allWindows and true or false
if self:isUnresponsive() then return false end
local win = self:focusedWindow()
if win then
return win:becomeMain() and self:_bringtofront(allWindows)
else
return self:_activate(allWindows)
end
end
--- hs.application:name()
--- Method
--- Alias for [`hs.application:title()`](#title)
objectMT.name=objectMT.title
--- hs.application.get(hint) -> hs.application object
--- Constructor
--- Gets a running application
---
--- Parameters:
--- * hint - search criterion for the desired application; it can be:
--- - a pid number as per `hs.application:pid()`
--- - a bundle ID string as per `hs.application:bundleID()`
--- - an application name string as per `hs.application:name()`
---
--- Returns:
--- * an hs.application object for a running application that matches the supplied search criterion, or `nil` if not found
---
--- Notes:
--- * see also `hs.application.find`
function application.get(hint)
return tpack(application.find(hint,true),nil)[1] -- just to be sure, discard extra results
end
--- hs.application.find(hint) -> hs.application object(s)
--- Constructor
--- Finds running applications
---
--- Parameters:
--- * hint - search criterion for the desired application(s); it can be:
--- - a pid number as per `hs.application:pid()`
--- - a bundle ID string as per `hs.application:bundleID()`
--- - a string pattern that matches (via `string.find`) the application name as per `hs.application:name()` (for convenience, the matching will be done on lowercased strings)
--- - a string pattern that matches (via `string.find`) the application's window title per `hs.window:title()` (for convenience, the matching will be done on lowercased strings)
---
--- Returns:
--- * one or more hs.application objects for running applications that match the supplied search criterion, or `nil` if none found
---
--- Notes:
--- * If multiple results are found, this function will return multiple values. See [https://www.lua.org/pil/5.1.html](https://www.lua.org/pil/5.1.html) for more information on how to work with this
--- * for convenience you can call this as `hs.application(hint)`
--- * use this function when you don't know the exact name of an application you're interested in, i.e.
--- from the console: `hs.application'term' --> hs.application: iTerm2 (0x61000025fb88) hs.application: Terminal (0x618000447588)`.
--- But be careful when using it in your `init.lua`: `terminal=hs.application'term'` will assign either "Terminal" or "iTerm2" arbitrarily (or even,
--- if neither are running, any other app with a window that happens to have "term" in its title); to make sure you get the right app in your scripts,
--- use `hs.application.get` with the exact name: `terminal=hs.application.get'Terminal' --> "Terminal" app, or nil if it's not running`
---
--- Usage:
--- -- by pid
--- hs.application(42):name() --> Finder
--- -- by bundle id
--- hs.application'com.apple.Safari':name() --> Safari
--- -- by name
--- hs.application'chrome':name() --> Google Chrome
--- -- by window title
--- hs.application'bash':name() --> Terminal
local findSpotlightWarningGiven = false
function application.find(hint,exact)
if hint==nil then return end
local typ=type(hint)
if typ=='number' then return application.applicationForPID(hint)
elseif typ~='string' then error('hint must be a number or string',2) end
local r=application.applicationsForBundleID(hint)
if #r>0 then return tunpack(r) end
local apps=application.runningApplications()
if exact then for _,a in ipairs(apps) do if a:name()==hint then r[#r+1]=a end end
else for _,a in ipairs(apps) do local aname=a:name() if aname and aname:lower():find(hint:lower()) then r[#r+1]=a end end end
if spotlightEnabled then
for _, v in ipairs(table.pack(realNameFor(hint, exact))) do
for _, a in ipairs(apps) do
if a:name() ~= nil and v:lower() == a:name():lower() then
r[#r+1]=a
end
end
end
elseif type(spotlightEnabled) == "nil" and not findSpotlightWarningGiven then
findSpotlightWarningGiven = true
print("-- Some applications have alternate names which can also be checked if you enable Spotlight support with `hs.application.enableSpotlightForNameSearches(true)`.")
end
tsort(r,function(a,b)return a:kind()>b:kind()end) -- gui apps first
if exact or #r>0 then return tunpack(r) end
r=tpack(hs.window.find(hint))
local rs={} for _,w in ipairs(r) do rs[w:application()]=true end -- :toSet
for a in pairs(rs) do r[#r+1]=a end -- and back, no dupes
if #r>0 then return tunpack(r) end
end
--- hs.application:findWindow(titlePattern) -> hs.window object(s)
--- Method
--- Finds windows from this application
---
--- Parameters:
--- * titlePattern - a string pattern that matches (via `string.find`) the window title(s) as per `hs.window:title()` (for convenience, the matching will be done on lowercased strings)
---
--- Returns:
--- * one or more hs.window objects belonging to this application that match the supplied search criterion, or `nil` if none found
function objectMT.findWindow(self, hint)
return hs.window.find(hint,false,self:allWindows())
end
--- hs.application:getWindow(title) -> hs.window object
--- Method
--- Gets a specific window from this application
---
--- Parameters:
--- * title - the desired window's title string as per `hs.window:title()`
---
--- Returns:
--- * the desired hs.window object belonging to this application, or `nil` if not found
function objectMT.getWindow(self, hint)
return tpack(hs.window.find(hint,true,self:allWindows()),nil)[1]
end
--- hs.application.open(app[, wait, [waitForFirstWindow]]) -> hs.application object
--- Constructor
--- Launches an application, or activates it if it's already running
---
--- Parameters:
--- * app - a string describing the application to open; it can be:
--- - the application's name as per `hs.application:name()`
--- - the full path to an application on disk (including the `.app` suffix)
--- - the application's bundle ID as per `hs.application:bundleID()`
--- * wait - (optional) the maximum number of seconds to wait for the app to be launched, if not already running; if omitted, defaults to 0;
--- if the app takes longer than this to launch, this function will return `nil`, but the app will still launch
--- * waitForFirstWindow - (optional) if `true`, additionally wait until the app has spawned its first window (which usually takes a bit longer)
---
--- Returns:
--- * the `hs.application` object for the launched or activated application; `nil` if not found
---
--- Notes:
--- * the `wait` parameter will *block all Hammerspoon activity* in order to return the application object "synchronously"; only use it if you
--- a) have no time-critical event processing happening elsewhere in your `init.lua` and b) need to act on the application object, or on
--- its window(s), right away
--- * when launching a "windowless" app (background daemon, menulet, etc.) make sure to omit `waitForFirstWindow`
function application.open(app,wait,waitForWindow)
if type(app)~='string' then error('app must be a string',2) end
if wait and type(wait)~='number' then error('wait must be a number',2) end
local r=application.launchOrFocus(app) or application.launchOrFocusByBundleID(app)
if not r then return end
r=nil
wait=(wait or 0)*1000000
local CHECK_INTERVAL=100000
repeat
r=r or application.get(app)
if r and (not waitForWindow or r:mainWindow()) then return r end
timer.usleep(math.min(wait,CHECK_INTERVAL)) wait=wait-CHECK_INTERVAL
until wait<=0
return r
end
--- hs.application.menuGlyphs
--- Variable
--- A table containing UTF8 representations of the defined key glyphs used in Menus for keybaord shortcuts which are presented pictorially rather than as text (arrow keys, return key, etc.)
---
--- These glyphs are indexed numerically where the numeric index matches a possible value for the AXMenuItemCmdGlyph key of an entry returned by `hs.application.getMenus`. If the AXMenuItemCmdGlyph field is non-numeric, then no glyph is used in the presentation of the keyboard shortcut for a menu item.
---
--- The following glyphs are defined:
--- * "⇥", -- kMenuTabRightGlyph, 0x02, Tab to the right key (for left-to-right script systems)
--- * "⇤", -- kMenuTabLeftGlyph, 0x03, Tab to the left key (for right-to-left script systems)
--- * "⌤", -- kMenuEnterGlyph, 0x04, Enter key
--- * "⇧", -- kMenuShiftGlyph, 0x05, Shift key
--- * "⌃", -- kMenuControlGlyph, 0x06, Control key
--- * "⌥", -- kMenuOptionGlyph, 0x07, Option key
--- * "␣", -- kMenuSpaceGlyph, 0x09, Space (always glyph 3) key
--- * "⌦", -- kMenuDeleteRightGlyph, 0x0A, Delete to the right key (for right-to-left script systems)
--- * "↩", -- kMenuReturnGlyph, 0x0B, Return key (for left-to-right script systems)
--- * "↪", -- kMenuReturnR2LGlyph, 0x0C, Return key (for right-to-left script systems)
--- * "", -- kMenuPencilGlyph, 0x0F, Pencil key
--- * "↓", -- kMenuDownwardArrowDashedGlyph, 0x10, Downward dashed arrow key
--- * "⌘", -- kMenuCommandGlyph, 0x11, Command key
--- * "✓", -- kMenuCheckmarkGlyph, 0x12, Checkmark key
--- * "⃟", -- kMenuDiamondGlyph, 0x13, Diamond key
--- * "", -- kMenuAppleLogoFilledGlyph, 0x14, Apple logo key (filled)
--- * "⌫", -- kMenuDeleteLeftGlyph, 0x17, Delete to the left key (for left-to-right script systems)
--- * "←", -- kMenuLeftArrowDashedGlyph, 0x18, Leftward dashed arrow key
--- * "↑", -- kMenuUpArrowDashedGlyph, 0x19, Upward dashed arrow key
--- * "→", -- kMenuRightArrowDashedGlyph, 0x1A, Rightward dashed arrow key
--- * "⎋", -- kMenuEscapeGlyph, 0x1B, Escape key
--- * "⌧", -- kMenuClearGlyph, 0x1C, Clear key
--- * "『", -- kMenuLeftDoubleQuotesJapaneseGlyph, 0x1D, Unassigned (left double quotes in Japanese)
--- * "』", -- kMenuRightDoubleQuotesJapaneseGlyph, 0x1E, Unassigned (right double quotes in Japanese)
--- * "␢", -- kMenuBlankGlyph, 0x61, Blank key
--- * "⇞", -- kMenuPageUpGlyph, 0x62, Page up key
--- * "⇪", -- kMenuCapsLockGlyph, 0x63, Caps lock key
--- * "←", -- kMenuLeftArrowGlyph, 0x64, Left arrow key
--- * "→", -- kMenuRightArrowGlyph, 0x65, Right arrow key
--- * "↖", -- kMenuNorthwestArrowGlyph, 0x66, Northwest arrow key
--- * "﹖", -- kMenuHelpGlyph, 0x67, Help key
--- * "↑", -- kMenuUpArrowGlyph, 0x68, Up arrow key
--- * "↘", -- kMenuSoutheastArrowGlyph, 0x69, Southeast arrow key
--- * "↓", -- kMenuDownArrowGlyph, 0x6A, Down arrow key
--- * "⇟", -- kMenuPageDownGlyph, 0x6B, Page down key
--- * "", -- kMenuContextualMenuGlyph, 0x6D, Contextual menu key
--- * "⌽", -- kMenuPowerGlyph, 0x6E, Power key
--- * "F1", -- kMenuF1Glyph, 0x6F, F1 key
--- * "F2", -- kMenuF2Glyph, 0x70, F2 key
--- * "F3", -- kMenuF3Glyph, 0x71, F3 key
--- * "F4", -- kMenuF4Glyph, 0x72, F4 key
--- * "F5", -- kMenuF5Glyph, 0x73, F5 key
--- * "F6", -- kMenuF6Glyph, 0x74, F6 key
--- * "F7", -- kMenuF7Glyph, 0x75, F7 key
--- * "F8", -- kMenuF8Glyph, 0x76, F8 key
--- * "F9", -- kMenuF9Glyph, 0x77, F9 key
--- * "F10", -- kMenuF10Glyph, 0x78, F10 key
--- * "F11", -- kMenuF11Glyph, 0x79, F11 key
--- * "F12", -- kMenuF12Glyph, 0x7A, F12 key
--- * "F13", -- kMenuF13Glyph, 0x87, F13 key
--- * "F14", -- kMenuF14Glyph, 0x88, F14 key
--- * "F15", -- kMenuF15Glyph, 0x89, F15 key
--- * "⎈", -- kMenuControlISOGlyph, 0x8A, Control key (ISO standard)
--- * "⏏", -- kMenuEjectGlyph, 0x8C, Eject key (available on Mac OS X 10.2 and later)
--- * "英数", -- kMenuEisuGlyph, 0x8D, Japanese eisu key (available in Mac OS X 10.4 and later)
--- * "かな", -- kMenuKanaGlyph, 0x8E, Japanese kana key (available in Mac OS X 10.4 and later)
--- * "F16", -- kMenuF16Glyph, 0x8F, F16 key (available in SnowLeopard and later)
--- * "F17", -- kMenuF16Glyph, 0x90, F17 key (available in SnowLeopard and later)
--- * "F18", -- kMenuF16Glyph, 0x91, F18 key (available in SnowLeopard and later)
--- * "F19", -- kMenuF16Glyph, 0x92, F19 key (available in SnowLeopard and later)
---
--- Notes:
--- * a `__tostring` metamethod is provided for this table so you can view its current contents by typing `hs.application.menuGlyphs` into the Hammerspoon console.
--- * This table is provided as a variable so that you can change any representation if you feel you know of a better or more appropriate one for you usage at runtime.
---
--- * The glyphs provided are defined in the Carbon framework headers in the Menus.h file, located (as of 10.11) at /System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Headers/Menus.h.
--- * The following constants are defined in Menus.h, but do not seem to correspond to a visible UTF8 character or well defined representation that I could discover. If you believe that you know of a (preferably sanctioned by Apple) proper visual representation, please submit an issue detailing it at the Hammerspoon repository on Github.
--- * kMenuNullGlyph, 0x00, Null (always glyph 1)
--- * kMenuNonmarkingReturnGlyph, 0x0D, Nonmarking return key
--- * kMenuParagraphKoreanGlyph, 0x15, Unassigned (paragraph in Korean)
--- * kMenuTrademarkJapaneseGlyph, 0x1F, Unassigned (trademark in Japanese)
--- * kMenuAppleLogoOutlineGlyph, 0x6C, Apple logo key (outline)
application.menuGlyphs = setmetatable({
[2] = "", -- kMenuTabRightGlyph, 0x02, Tab to the right key (for left-to-right script systems)
[3] = "", -- kMenuTabLeftGlyph, 0x03, Tab to the left key (for right-to-left script systems)
[4] = "", -- kMenuEnterGlyph, 0x04, Enter key
[5] = "", -- kMenuShiftGlyph, 0x05, Shift key
[6] = "", -- kMenuControlGlyph, 0x06, Control key
[7] = "", -- kMenuOptionGlyph, 0x07, Option key
[9] = "", -- kMenuSpaceGlyph, 0x09, Space (always glyph 3) key
[10] = "", -- kMenuDeleteRightGlyph, 0x0A, Delete to the right key (for right-to-left script systems)
[11] = "", -- kMenuReturnGlyph, 0x0B, Return key (for left-to-right script systems)
[12] = "", -- kMenuReturnR2LGlyph, 0x0C, Return key (for right-to-left script systems)
[15] = "", -- kMenuPencilGlyph, 0x0F, Pencil key
[16] = "", -- kMenuDownwardArrowDashedGlyph, 0x10, Downward dashed arrow key
[17] = "", -- kMenuCommandGlyph, 0x11, Command key
[18] = "", -- kMenuCheckmarkGlyph, 0x12, Checkmark key
[19] = "", -- kMenuDiamondGlyph, 0x13, Diamond key
[20] = "", -- kMenuAppleLogoFilledGlyph, 0x14, Apple logo key (filled)
[23] = "", -- kMenuDeleteLeftGlyph, 0x17, Delete to the left key (for left-to-right script systems)
[24] = "", -- kMenuLeftArrowDashedGlyph, 0x18, Leftward dashed arrow key
[25] = "", -- kMenuUpArrowDashedGlyph, 0x19, Upward dashed arrow key
[26] = "", -- kMenuRightArrowDashedGlyph, 0x1A, Rightward dashed arrow key
[27] = "", -- kMenuEscapeGlyph, 0x1B, Escape key
[28] = "", -- kMenuClearGlyph, 0x1C, Clear key
[29] = "", -- kMenuLeftDoubleQuotesJapaneseGlyph, 0x1D, Unassigned (left double quotes in Japanese)
[30] = "", -- kMenuRightDoubleQuotesJapaneseGlyph, 0x1E, Unassigned (right double quotes in Japanese)
[97] = "", -- kMenuBlankGlyph, 0x61, Blank key
[98] = "", -- kMenuPageUpGlyph, 0x62, Page up key
[99] = "", -- kMenuCapsLockGlyph, 0x63, Caps lock key
[100] = "", -- kMenuLeftArrowGlyph, 0x64, Left arrow key
[101] = "", -- kMenuRightArrowGlyph, 0x65, Right arrow key
[102] = "", -- kMenuNorthwestArrowGlyph, 0x66, Northwest arrow key
[103] = "", -- kMenuHelpGlyph, 0x67, Help key
[104] = "", -- kMenuUpArrowGlyph, 0x68, Up arrow key
[105] = "", -- kMenuSoutheastArrowGlyph, 0x69, Southeast arrow key
[106] = "", -- kMenuDownArrowGlyph, 0x6A, Down arrow key
[107] = "", -- kMenuPageDownGlyph, 0x6B, Page down key
[109] = "", -- kMenuContextualMenuGlyph, 0x6D, Contextual menu key
[110] = "", -- kMenuPowerGlyph, 0x6E, Power key
[111] = "F1", -- kMenuF1Glyph, 0x6F, F1 key
[112] = "F2", -- kMenuF2Glyph, 0x70, F2 key
[113] = "F3", -- kMenuF3Glyph, 0x71, F3 key
[114] = "F4", -- kMenuF4Glyph, 0x72, F4 key
[115] = "F5", -- kMenuF5Glyph, 0x73, F5 key
[116] = "F6", -- kMenuF6Glyph, 0x74, F6 key
[117] = "F7", -- kMenuF7Glyph, 0x75, F7 key
[118] = "F8", -- kMenuF8Glyph, 0x76, F8 key
[119] = "F9", -- kMenuF9Glyph, 0x77, F9 key
[120] = "F10", -- kMenuF10Glyph, 0x78, F10 key
[121] = "F11", -- kMenuF11Glyph, 0x79, F11 key
[122] = "F12", -- kMenuF12Glyph, 0x7A, F12 key
[135] = "F13", -- kMenuF13Glyph, 0x87, F13 key
[136] = "F14", -- kMenuF14Glyph, 0x88, F14 key
[137] = "F15", -- kMenuF15Glyph, 0x89, F15 key
[138] = "", -- kMenuControlISOGlyph, 0x8A, Control key (ISO standard)
[140] = "", -- kMenuEjectGlyph, 0x8C, Eject key (available on Mac OS X 10.2 and later)
[141] = "英数", -- kMenuEisuGlyph, 0x8D, Japanese eisu key (available in Mac OS X 10.4 and later)
[142] = "かな", -- kMenuKanaGlyph, 0x8E, Japanese kana key (available in Mac OS X 10.4 and later)
[143] = "F16", -- kMenuF16Glyph, 0x8F, F16 key (available in SnowLeopard and later)
[144] = "F17", -- kMenuF16Glyph, 0x90, F17 key (available in SnowLeopard and later)
[145] = "F18", -- kMenuF16Glyph, 0x91, F18 key (available in SnowLeopard and later)
[146] = "F19", -- kMenuF16Glyph, 0x92, F19 key (available in SnowLeopard and later)
}, {
__tostring = function(self)
local result = ""
for k, v in require("hs.fnutils").sortByKeys(self) do
result = result..string.format("%4d %s\n", k, v)
end
return result
end,
})
-- handles updates to the alternateNameMap table
local modifyNameMap = function(info, add)
for _, item in ipairs(info) do
local applicationName = item.kMDItemFSName
for _, alt in ipairs(item.kMDItemAlternateNames or {}) do
alternateNameMap[alt:match("^(.*)%.app$") or alt] = add and applicationName or nil
end
end
end
-- local var to hold spotlight query userdata to catch updates
local spotlightWatcher
-- starts the spotlight query to get the alternate names for applications
local buildAlternateNameMap = function()
if spotlightWatcher then -- force a rebuild if it's already running
spotlightWatcher:stop()
spotlightWatcher = nil
alternateNameMap = {}
application._alternateNameMap = alternateNameMap
end
spotlightWatcher = require"hs.spotlight".new()
spotlightWatcher:queryString([[ kMDItemContentType = "com.apple.application-bundle" ]])
:callbackMessages("didUpdate", "inProgress")
:setCallback(function(_, _, info)
if info then -- shouldn't be nil for didUpdate and inProgress, but check anyways
-- all three can occur in either message, so check them all!
if info.kMDQueryUpdateAddedItems then
modifyNameMap(info.kMDQueryUpdateAddedItems, true)
end
if info.kMDQueryUpdateChangedItems then
modifyNameMap(info.kMDQueryUpdateChangedItems, true)
end
if info.kMDQueryUpdateRemovedItems then
modifyNameMap(info.kMDQueryUpdateRemovedItems, false)
end
end
end):start()
end
--- hs.application.enableSpotlightForNameSearches([state]) -> boolean
--- Function
--- Get or set whether Spotlight should be used to find alternate names for applications.
---
--- Parameters:
--- * `state` - an optional boolean specifying whether or not Spotlight should be used to try and determine alternate application names for `hs.application.find` and similar functions.
---
--- Returns:
--- * the current, possibly changed, state
---
--- Notes:
--- * This setting is persistent across reloading and restarting Hammerspoon.
--- * If this was set to true and you set it to true again, it will purge the alternate name map and rebuild it from scratch.
--- * You can disable Spotlight alternate name mapping by setting this value to false or nil. If you set this to false, then the notifications indicating that more results might be possible if Spotlight is enabled will be suppressed.
application.enableSpotlightForNameSearches = function(...)
local args = table.pack(...)
if args.n > 0 then
if args[1] then
settings.set("HSenableSpotlightForNameSearches", true)
spotlightEnabled = true
buildAlternateNameMap()
else
settings.set("HSenableSpotlightForNameSearches", args[1])
spotlightEnabled = args[1]
if spotlightWatcher then
spotlightWatcher:stop()
spotlightWatcher = nil
end
alternateNameMap = {}
application._alternateNameMap = alternateNameMap
end
end
return settings.get("HSenableSpotlightForNameSearches")
end
-- if the setting is set, then go ahead and start the build process
if spotlightEnabled then buildAlternateNameMap() end
do
local mt=getmetatable(application)
-- whoever gets it first (window vs application)
if not mt.__call then mt.__call=function(t,...) return t.find(...) end end
end
application._alternateNameMap = alternateNameMap
return application