Previous Page
Next Page

11.9. Element Specifiers

Referring to a property is easy; you just use the name of the property. For example:

get version of application "Finder" -- 10.4.2

Referring to an element is harder. An object can have any number of each class of element, so you must say which one(s) you mean. To do this, you use an element specifier (or just specifier for shortAppleScript also calls this a key form). A specifier has two components: the name of a class and some way of picking out the right one(s). AppleScript has eight built-in forms of specifier, and these are the only ones you are allowed to use. The next eight sections describe those eight specifier forms.

(Actually, there are actually nine element specifiers. I don't discuss middle because it is rarely used. Plus, a reference to a property is actually a form of specifier, so I guess that makes ten. The variety of specifier forms makes a specifier quite an interesting and complicated part of an Apple event. The repeated pattern involving the four terms form, want, seld, and from in Example 3-1 denotes a specifier.)

In real life, it will rarely be open to you to use just whichever specifier form you please on a particular occasion. Given a certain application, object, and class of element, only certain specifier forms will work, and experimentation is the best guide as to which ones they are. An application's dictionary is supposed to help you here, but it might not, or might not be accurate (see "Defective Element Specifiers" in Chapter 20).

11.9.1. Name

An element may have a name, which is some kind of string. (I say "some kind of" because it might, for example, be Unicode text, which is not the same class as string.) To specify an element by name, say the class followed by the name:

tell application "Finder" to get disk "main"

You may insert the keyword named between the class and the name, but I never do.

Typically, there is also a name property, so that you can learn, based on some other element specifier, how to specify a particular element by name:

tell application "Finder" to get name of disk 2 -- "gromit"

11.9.2. Index

Elements are usually ordered, and numbered in accordance with this ordering. The number is an index . The first element has index 1, the next element has index 2, and so forth. The last element can be referred to by index -1, the next-to-last by index -2, and so forth. (If you want to know just how many elements of this class there are, you have to find out in some other way, such as count.)

To specify an element by index, say the class followed by the index number:

tell application "Finder" to get disk 2

You may insert the keyword index between the class and the number, but I never do. Instead of a cardinal number, you're allowed to use a wide variety of English-like ordinal numeric literals followed by the class name. So you can say such things as 1st disk, third disk, last disk, front disk, and back disk.

There is sometimes also an index property, so that you can learn, based on some other element specifier, how to specify a particular element by index, but this is not implemented anywhere near as often as one would like, and is sometimes buggy:

tell application "Finder" to get index of disk 2 -- 3, for heaven's sake

11.9.3. ID

Elements may have a unique and unchanging ID, which is often a number but needn't be. For example, in Entourage a message's name can be changed, and its index within its folder may change, but its ID is constant.

To specify an element by ID, say the class followed by the keyword id followed by the ID value. This value will have been obtained at some earlier point, typically by asking for an element's ID property:

tell application "Microsoft Entourage"
    set messageID to ID of message 1 of in box folder -- 2849, if you must know
    -- more code goes here...
    get message id messageID
end tell

11.9.4. Some

A random element may be specified by saying some followed by the class:

tell application "Finder"
    name of some disk -- "gromit"
    name of some disk -- "feathers"
    name of some disk -- "feathers"
end tell

11.9.5. Every

It may be possible to get a list of every element of a class. To ask for such a list, say the keyword every followed by the class; alternatively, you may be able to say just the plural of the class:

tell application "Finder" to get every disk
tell application "Finder" to get disks

If asking for just one element would result in a reference, the result in this case is a list of references.

11.9.6. Range

Elements may be ordered, and you may be able to obtain a list of contiguous elements (a range ) by giving the first and last index number you're interested in. It is generally not important in what order you give these index numbers.

To specify elements by range, say the class in a plural form (or every and the class) followed by an index number, the keyword thru (or through), and another index number. You can say beginning or end instead of an index number:

get words 1 thru 4 of "now is the winter of our discontent"
get words beginning thru 4 of "now is the winter of our discontent"

Alternatively, you may be able to get a list of contiguous elements of a class where the range is marked off by two element specifiers for some other class. In this case, you say the class in a plural form (or every and the class) followed by the keyword from, an element specifier for the starting point, the keyword to, and an element specifier for the ending point. Again, you can say beginning or end instead of an element specifier:

get words from character 12 to character 17 of "now is the winter"
get words from character 12 to character -1 of "now is the winter"
get words from character 12 to end of "now is the winter"

There is a tendency to confuse or conflate these two forms, and to try to say something like this:

get words 1 to 3 of "now is the winter of our discontent" -- compile-time error

You can't do that. "To" is not "thru"! Keep these two constructions straight. Practice them before going to bed.

11.9.7. Relative

Elements may be ordered, and it may be possible to refer to an element as the successor or predecessor of another element. To ask for an element in this way, say the name of the class, the keyword before or after, and an element specifier:

tell application "TextEdit"
    tell document 1
        get word after word 1
    end tell
end tell

A synonym for before is in front of. Synonyms for after are behind and in back of.

That wasn't a very useful example, because we could have asked for word 2. But relative specifiers are useful when you can obtain an object of one class in positional relation to another. For example, in BBEdit all text has insertion point elements lying between the characters. Thus you can say this:

tell application "BBEdit"
    tell text of window 1
        get insertion point before word 4
    end tell
end tell

Given an insertion point in BBEdit, you can set its contents property to alter the text. This code changes "This is a test" to "This is a great test":

tell application "BBEdit"
    tell text of window 1
        set pt to insertion point before word 4
        set contents of pt to "great "
    end tell
end tell

Similarly, some applications have a class called location reference (or "insertion location ," depending on how the dictionary is displayed). Unlike BBEdit's insertion point, you usually can't get one directly; instead, you form one, primarily as the at or to parameter of the duplicate, make, and move commands. An insertion location is specified using before or after and an element specifier (yielding bizarre locutions like at after); or using beginning of or end of and a reference to an object, or just beginning or end alone; or using just an element specifier.

Applications can be extraordinarily touchy about how they expect an insertion location to look, with results differing from application to application. Here are some examples:

tell application "TextEdit"
    tell text of document 1
        make new word at after word 2 with data "not "
        -- changes "this is a test" to "this is not a test"
    end tell
end tell
 
tell application "TextEdit"
    tell text of document 1
        duplicate word 1 to end
        -- changes "fair is foul and foul is " to "fair is foul and foul is fair"
    end tell
end tell
 
tell application "TextEdit"
    tell text of document 1
        duplicate word 1 to beginning of word 3
        -- changes "wonder of s" to "wonder of wonders"
    end tell
end tell
 
tell application "TextEdit"
    tell text of document 1
        duplicate word 1 to word 7
        -- changes "fair is foul and foul is foul" to "fair is foul and foul is fair"
    end tell
end tell
 
tell application "Script Debugger"
    move window 2 to beginning
    -- bring the second window frontmost
end tell

That last example shows that this locution manipulates more than text.

11.9.8. Boolean Test

It may be possible to get a list of those elements that satisfy a boolean test . What you test may be a property of the target, or it may be the target itself. The test may involve any boolean operator (see "Comparison Operators" and "Containment Operators" in Chapter 15).

A boolean test also involves an index-based specifier: index, range, or every (or some). This is because the answer is a list, and you can ask for the whole list or for particular elements of it.

To specify elements by boolean test, start with an index-based specifier for a class and the keyword where (or whose), followed by a property of that class or the word it, followed by a boolean operator and any value that can function as that operator's second operand. In this context, the word it means the element to be tested. How you select among the various synonyms and options will probably depend on what feels most English-like to you:

tell application "Finder" to get files where name begins with "s"
tell application "Finder" to get every file where name begins with "s"
tell application "Finder" to get files where name of it begins with "s"
tell application "Finder" to get files where its name begins with "s"
tell application "Finder" to get files whose name begins with "s"

Those are all equivalent. The index-based specifier is the every specifier, with file as its class; the plural files is a synonym. Then comes where or whose. Now every file will be tested, and name means the name property of the file being tested; the words of it or its are redundant but harmless. The boolean operator is begins with, and its second operand is the string "s".

When a boolean test specifier tests the value of a boolean property, you can say it is (or it is not) and the name of the property. Doing so can make your expression more English-like (if the property name is an adjective); the following two formulations are equivalent, the latter being more English-like:

tell application "System Events"
    get process 1 whose frontmost is true
    get process 1 where it is frontmost
end tell

The keyword it is needed when an application has defined a property with the same name as a class, just as we saw earlier ("It"). The problem is troublesome, because there's no error; you just don't get the right answer. This comes up empty:

tell application "Microsoft Entourage"
    get every POP account whose email address contains "matt" -- {}
end tell

Saying its disambiguates. You might also change whose to where for the sake of English-likeness, but you don't have to:

tell application "Microsoft Entourage"
    get every POP account whose its email address contains "matt"
    -- {POP account id 1...}
end tell

You definitely need it when what you want to test is each object itself, not a property of it:

tell application "TextEdit"
    tell text of document 1
        get every word where it contains "t"
        get words whose it contains "t"
    end tell
end tell

The two formulations shown are equivalent, but most people prefer the former, as being more English-like. (AppleScript has no equivalent for where it, such as "which" or "that"; it doesn't let you be as English-like as all that.)

That example works because when you ask TextEdit for words, you get a list of strings; each string then functions as the first operand of contains. But this is by no means how every application works. For example, in BBEdit, when you ask for words you get a list of references. To obtain the text of each word you ask for its contents, so you don't end up using it at all:

tell application "BBEdit"
    tell text 1 of window 1
        get every word whose contents contains "t"
    end tell
end tell

Finally, here's an example involving an index specifier other than every:

tell application "BBEdit"
    tell text 1 of window 1
        get word 1 whose contents contains "t"
    end tell
end tell

When you use every, if no elements satisfy the text, you get an empty list. But when you specify the index, if that index doesn't exist, you get an error. (This is because you get an error if you ask for a nonexistent item of any list.) You can easily catch such an error and handle it (see Chapter 19), but it's something to watch out for.

If the target application is willing, you may even be able to combine multiple boolean tests (see "Boolean Operators" in Chapter 15). The syntax rules require that you supply a first operand for the second test, even if this is the same as the first operand of the first test:

tell application "TextEdit"
    tell document 1
        words where it begins with "t" and it ends with "t" -- {"test"}
    end tell
end tell

The boolean test, where it works, is a very powerful specifier; with a single Apple event you're getting the target application to do a lot of searching for you. Unfortunately, you never know whether it will work; only experimentation will tell you.

AppleScript itself, most disappointingly, fails to implement boolean test specifiers for its own lists. The two halves of this example are parallel, yet the second fails:

tell application "Finder"
    get every disk whose name begins with "g" -- "gromit"
end tell
set pepBoys to {"Mannie", "Moe", "Jack"}
tell pepBoys
    get every item whose text begins with "M" -- error: Can't get...
end tell

As a workaround, use the list-filtering handler developed earlier in this book (under "Power Handler Tricks" in Chapter 9).


Previous Page
Next Page