-
Notifications
You must be signed in to change notification settings - Fork 0
aelib
Before you can communicate with a scriptable application, you must obtain an app object. For now, use the app
command, passing the app’s bundle ID:
app {bundle_identifier as string}
For example:
set Finder to app “com.apple.finder”
set TextEdit to app “com.apple.textedit”
set Music to app “com.apple.Music”
Use tell TARGET to ACTION
to direct commands to an application:
tell Finder to open home « open the home folder in a window »
tell TextEdit to do
activate
set d to make new: #document at: end of documents
make new: #paragraph at: end of paragraphs of d with_data: “Hello”
done
BUG: set…to…
isn't delegating correctly when left operand is not a reference (i.e. it should bind the result of make
to the name d
in the parent scope, the app’s set
handler effectively operating as a multimethod that dispatches on its left operand being an app-defined command or local name); alternatively, set
could be reserved for external targets only and label:value
notation used to bind locally
Unlike AppleScript’s tell
statement, which redirects all commands to the target object, iris sends commands which are not handled by the target object to the parent scope as normal.
Tell blocks can be nested, as in AppleScript:
tell Finder to do
tell home to do
tell folders to do
get name
done
done
done
All applications should respond to the following commands:
-
run
-- Run an application. Most applications will open an empty, untitled window. -
launch
-- Launch an application without sending it arun
event. Applications that normally open a new, empty document upon launch won't do so. -
activate
-- Bring the application to the front. -
reopen
-- Reactivate a running application. Some applications will open a new untitled window if no window is open. -
open {value}
-- Open the specified object(s). The value is list of objects to open, typically a list ofPath
objects. -
print {value}
-- Print the specified object(s). The value is a list of objects to print, typically a list ofPath
objects. -
quit {saving: value as choice [#yes, #ask, #no]}
-- Quit an application. Thesaving
value indicates whether or not any currently open documents should be saved before quitting.
Some applications may provide their own definitions of some or all of these commands, so check their terminology before use.
aelib also defines get
and set
commands for any scriptable application that doesn't supply its own definitions:
get { direct_parameter as reference } returning anything
set { direct_parameter as reference, to: value as anything }
You can check if the application specified by an Application object is currently running by getting its is_running
property. This is useful if you don't want to perform commands on an application that isn't already running. For example:
tell app “com.apple.textedit” to do
« Only perform TextEdit-related commands if it's already running: »
if is_running then do
« all TextEdit-related code goes here... »
done
done
If the application can't be launched for some reason (e.g. if it's in the Trash), a "can't launch application" error will be raised. This provides a description of the problem (if it's a standard LaunchServices error) along with the original OS error number.
An object property contains one of the following:
- a simple value (number, string, array, etc.) that describes an attribute of that object, such as its name, class, size, or, color.
- an object specifier that represents a one-to-one relationship between this object and another object within the application's object model, providing a convenient shortcut to another object of particular interest to users; for example, the Finder's
startup_disk and
home
properties identify the current user's startup disk and home folder, Music'scurrent_track
property contains a object specifier that identifies the currently playing track.
Syntax:
property of reference
Examples:
text of document at 1 of TextEdit
current_track of Music
name of every file of home of Finder
Many objects also have elements, which represent a one-to-many relationship between that object and others.
Object elements often mirror the application's underlying hierarchical data model; for example, the Finder's application
object contains one or more disk
objects, which can contain any number of file
and/or folder
objects, which may contain further file
and/or folder
objects, and so on, just as the file system itself is structured:
files of … folders of disks of finder
At other times, they may represent relationships provided simply as a convenience to scripters, e.g. the Finder's application
object also provides file
and folder
elements as quick shortcuts to file
and folder
objects on the user's desktop; thus all these object specifiers identify the same file objects (assuming the current user is named jsmith
):
files of folder named “Desktop” of folder named “jsmith” of folder named “Users” of startup_disk of Finder
files of folder named “Desktop” of home of Finder
files of desktop of Finder
files of Finder
A element specifier uses the following syntax to identify all of an object's elements by default:
every element of reference
elements of reference
Examples:
folders of home of Finder
windows of TextEdit
every paragraph of every document of TextEdit
Once you've constructed an object specifier that identifies all of the elements of an object, you can further refine this selection to specify just one, or several, of those elements in particular. To do this, you call one (or sometimes more) of the following reference form methods:
at
named
id
-
first
/middle
/last
/any
-
previous
/next
-
from
(combined withthru
) where
-
beginning
,end
,before
,after
The following sections explain how to use each of these reference forms.
Identifies a single element object by its position.
Syntax:
elements at selector
The selector
value is a non-zero integer representing the object's index position. The first element has index 1
, the second element's index is 2
, the third is 3
, and so on. Negative numbers can be used to count backwards from the last element: -1
indicates the last element, -2
the second-to-last, and so on.
Examples:
words at 3
items at -1
Some applications also allow non-integer values to be used. For example, Finder also accepts a File
object that identify a file/folder/disk location anywhere in the file system:
app('Finder').items.at( new File('/path/to/some/file') )
Identifies the first element with the given name.
Syntax:
elements named selector
The selector
value is a string representing the object's name as given in its name
property.
Examples:
disk named “Macintosh HD”
file named “index.html”
Notes:
Applications usually treat object names as case-insensitive.
Where multiple element have the same name, a by-name reference only identifies the first element found with that name. If you wish to identify all elements with the same name, use where
instead.
Identifies a single element using a unique application-supplied key (typically an integer or string).
Syntax:
elements id selector
Examples
window id 4321
Identify the first, middle, or last element, or a randomly chosen element.
Syntax:
first elements
middle elements
last elements
any elements
Examples:
first document
last paragraph
any file
Identify a single element before or after (i.e. relative to) this one.
Syntax:
class_name before element
class_name after element
The class_name
value is a keyword object indicating the class of the nearest object to select (e.g. document
, folder
). For example, to specify the next folder after 'Music' in the user's home folder:
folder after folder named “Music” of home of Finder
Examples:
word after word at 3
character before last paragraph
Identifies all elements between and including the beginning and end of the range.
Syntax:
elements from start thru stop
The start
and stop
values may be integers, strings, and/or relative or absolute references.
The beginning and end elements are declared relative to the object containing the elements being selected. For example, the following reference indicate the third paragraph of the currrent container object:
paragraph at 3
Thus, to select every paragraph from the third through to the second-to-last:
paragraphs from paragraphs at 3 thru paragraph at -2
When the start and end elements are the same class as the elements being selected, which is frequently the case, thru
can be written much more concisely, like this:
paragraphs from 3 thru -1
thru
will automatically convert number and string arguments to relative references.
Some applications can understand quite complex range requests. For example, the following will work in Tex-Edit Plus:
words from character at 5 thru paragraph at -2 of first document
Examples:
documents from 1 thru 3
folders from “Downloads” thru “Movies”
text from character at 5 thru word at -2
Identifies zero or more elements whose properties and/or elements match one or more given tests. (In AppleScript, this is commonly known as a 'whose' (or 'where') clause.)
Syntax:
elements where test_reference
The test_reference
value is composed of the following:
-
One or more relative references that identify those properties and/or sub-elements to which a filter test should be applied.
-
For each of those references, the conditional test (e.g.
is_same_as
,is_in
) to apply to the value it identifies. -
If more than one test is being performed, or if a true result should produce false and vice-versa, Boolean-style logic tests (
and
/or
/not
) should also be used to combine them into a single compound test references.
Developer note: when evaluating its right operand, the where
handler overrides stdlib’s comparison/containment/logic handlers with its own implementations that accept and return relative references.
Comparison tests:
relative_reference is_before value « is less than »
relative_reference is_not_after value « is less than or equal to »
relative_reference is_same_as value « is equal to »
relative_reference is_not_same_as value « is not equal to »
relative_reference is_after value « is greater than »
relative_reference is_not_before value « is greater than or equal to »
TO DO: these operator names may change or be aliased; math operators might also be supported
Containment tests:
relative_reference begins_with value
relative_reference ends_with value
relative_reference contains value
relative_reference is_in value
The following logic tests are supported:
relative_reference and relative_reference
relative_reference or relative_reference
not relative_reference
Important: These are the only operations understood by the where
reference form. You cannot construct test clauses using other operators (e.g. +
, %
) or functions (trim
, round
), as the Apple Event Object Model does not recognize these and cannot perform them itself.
Examples:
name is_same_as “”
size > 1024
first word begins_with “A”
first character is_same_as last character
it is_not_same_as “”
size > 1000 and size < 100000
word at 1 begins_with “A” or word at 2 contains “ce” or word at 1 is_same_as “Foo”
TO DO: it
is not yet implemented
When testing certain classes of element, such as words and paragraphs, you can even apply a containment test directly to it. For example, to specify every word that isn't "Wednesday" within the front TextEdit document:
(words where it is_not_same_as “Wednesday”) of first document of TextEdit
Unlike other reference forms, which identify properties or elements, the insertion form identifies locations before/after/between an object's current elements.
Syntax:
beginning of elements
end of elements
before element
after element
Examples:
end of documents
before paragraph at 1
TO DO: how to pass event attributes (timeout, considering/ignoring, send options)? what about requested type?
To direct commands to an application, use a tell
block:
tell APP to COMMAND
tell APP to do
COMMAND
...
done
For example:
tell app “com.apple.music” to quit
### Command syntax
A command optionally accepts a single “arguments” object which contains the command's direct parameter and/or named parameters, plus any standard attributes (sendOptions
, withTimeout
, ignoring
). For example, to set the text of the frontmost TextEdit document:
tell TextEdit to set text of first document to "Hello, World!"
The following close
command closes all TextEdit documents, asking the user what to do about any unsaved changes:
tell TextEdit to close every document saving: #ask
tell TextEdit to activate
tell TextEdit to open paths « TO DO: how to identify fs objects »
tell Finder to get version
tell Finder to set name of file named "foo.txt" of home to "bar.txt"
tell TextEdit to count paragraphs of first document
tell TextEdit to make new: #document at: end of documents with_properties: {name: "README"}
tell Finder to get items of home as: #alias_list « TO DO: `as:` is currently unsupported in default terms and poorly/not supported by most apps »
TO DO: not yet implemented
Whereas an Apple event's parameters contain user data to be passed directly to the application's event handler for processing, its attributes control exactly how the Apple event should be sent and received. While the default values are sufficient in most cases, you can supply alternate values via the command's parameters object if required. The following attribute names are recognized:
-
ignoring
- a list of zero or more of the following keywords -k.case
,k.diacriticals
,k.hyphens
,k.punctuation
,k.whitespace
,k.numericStrings
- indicating which text attributes the application should consider or ignore when comparing text values itself. The default is[k.case]
, i.e. ignore case but consider all other attributes. (Note that most applications ignore text comparison flags and always use the default.) -
asType
- A keyword indicating the type of value the command should return as its result (assuming it knows how to produce values of that type). This attribute is supported by some application'sget
commands; for example, for example, to tell Finder to return an alias value (Path
) instead of a file/folder object specifier, useget({ asType: k.alias })
. -
timeout
- The number of seconds NodeAutomation should wait for the application to respond before giving up and reporting a timeout error. To wait forever, use0
. To use the default timeout (2 minutes), usenull
. [TBC] -
sendOptions
- [TBC]
If a make
command does not already have an at
parameter then the current (tell) target is used:
insertionSpecifier.make({ new: className })
is shorthand for:
applicationObject.make({ new: className, at: insertionSpecifier })
For example:
app('TextEdit').make({ new: k.document, at: app.documents.end });
is written more neatly as:
app('TextEdit').documents.end.make({ new: k.document });
The count
command doesn't exactly replicate AS's own behavior, which automatically adds an each:
parameter. Cocoa-based apps are unaffected but some Carbon-based apps (e.g.Finder) will error without it, in which case count some_reference each: #item
should be sufficient.
TO DO: errors are not yet fully implemented, nor the means to catch them
The CommandError exception describes an error raised by the target application or Apple Event Manager when sending a command.
CommandError(Exception)
Properties:
errorNumber : int -- Mac OS error number
errorMessage : str -- application-supplied/generic error description
offendingObject : anything | None -- object that caused the error,
if given by application
expectedType : anything | None -- object that caused a coercion error,
if given by application
partialResult : anything | None -- part of return value constructed
before error occurred, if given
by application
Methods:
[Symbol.toPrimitive](hint) -- Mac OS error number or formatted description of error
Unlike AppleScript, which implicitly sends a get
command to any unresolved references at the end of evaluating an expression, aelib only resolves a reference when it receives an appropriate command. For example:
tell app “com.apple.textedit” to do
set d to every document
done
is equivalent to (AppleScript):
tell application id "com.apple.textedit"
set d to a reference to every document
end tell
In these scripts, the value assigned to d
is a reference: every document of «TextEdit»
.
This is not the same as (AppleScript):
tell application id "com.apple.textedit"
set d to every document
end tell
Here, AppleScript resolves the reference to every document
by implicitly sending TextEdit a get
command, which returns a list of references. Whereas, iris only resolves references when explicitly told to do so:
tell app “com.apple.textedit” to do
set d to get every document
done