DISCLAIMER (September 22, 2023). — I was very unpleasantly surprised to find that almost all of the links mentioned in my original article were now obsolete, including resources as fundamental as those originally published by Apple about AppleScript. I therefore decided — since no one seems to care about saving old sources of information — to host on this server the mentioned material, i.e. the PDFs to which I refer for educational purposes. This undoubtedly constitutes a “copyright violation” in the eyes of a lawyer and I am fully aware of it, but I then respectfully request the rights holders to contact me and provide a RELIABLE link to refer my readers to. Thank you for your attention.


Specifier Validation

As previously stated, the system attempts to resolve a specifier each time a command is sent to InDesign through this specifier. But what exactly does that mean? Instinctively we all realized that a specifier has a recursive structure. Indeed we could describe any specifier by the following pattern:

    <SPECIFIER> := <CONTAINER> <SELECTOR>,

where <CONTAINER> is itself a reliable specifier and <SELECTOR> a symbolic link to reliable sub-element(s).

The <CONTAINER> can be app (the root specifier) or any expression that wraps a specifier, provided that the container is not empty.

The <SELECTOR> can be:
• Nothing (since any container is a specifier!)
• Any available property, array element, or method call, which results in a specifier. E.g.:
    .parent,
    .activeDocument,
    .parentStory,
    .allPageItems[0],
    .rectangles.add(),
    etc.
• Any available collection selector. E.g.:
    .stories[0],
    .documents.anyItem(),
    .characters.itemByRange(0,5),
    .pages.item('2'),
    .rectangles.everyItem(),
    etc.

By detecting actual InDesign object(s) behind the <CONTAINER>, the system dynamically checks for the validity of the container. It throws a runtime error if the container is empty. Anyway, the complete <SPECIFIER> remains unresolved at this stage: the system does not need to identify the actual receiver(s) as long as we don't send a command through the specifier. In other words, the specifier behaves as a pure symbolic link, as demonstrated by the following script:

//-------------------------------------------
// InDesign CS4/CS5
// Test this script with NO OPENED DOCUMENT !
//-------------------------------------------
 
// create 2 docs and 1 oval in doc2
var doc1 = app.documents.add(),
    doc2 = app.documents.add(),
    ov2 = doc2.ovals.add();
 
// create 3 groups in doc2
doc2.groups.add([ov2.duplicate(),ov2.duplicate()]);
doc2.groups.add([ov2.duplicate(),ov2.duplicate()]);
doc2.groups.add([ov2.duplicate(),ov2.duplicate()]);
 
// doc1 is still empty
// doc2 has 3 groups (but no rectangle within)
 
// spec is OK because one of the documents contains 3 groups
var spec = app.documents.everyItem().groups[2] // CONTAINER
    .rectangles.firstItem(); // SELECTOR
 
// but spec is invalid
// because there is no rectangle anywhere:
alert( spec.isValid );  // FALSE
 
// we can now remove all groups
// without causing any error!
doc2.groups.everyItem().remove();
 
// of course spec is still invalid:
alert( spec.isValid );  // FALSE
 
// then we create 3 rectangle groups in doc1:
var rec1 = doc1.rectangles.add();
doc1.groups.add([rec1.duplicate(),rec1.duplicate()]);
doc1.groups.add([rec1.duplicate(),rec1.duplicate()]);
doc1.groups.add([rec1.duplicate(),rec1.duplicate()]);
 
// spec is now valid:
alert( spec.isValid );  // TRUE
 

What is marvelous in the above code? There is absolutely no connection between the reliability of the specifier at the moment it is declared and the receiver to which it finally points out. When the spec variable is set, the system only needs to find a groups[2] somewhere. There are 3 groups in doc2, so the <CONTAINER> part of the specifier is temporarily checked as nonempty and no error is thrown. The specifier is invalid anyway, because the <SELECTOR> points out to nothing (there is no rectangle in the third group of doc2). Then the groups of doc2 are removed by doc2.groups.everyItem().remove()! At this stage no document contains any group, so the specifier really means nothing. And yet we can rely on its isValid property without error. Finally the script creates 3 groups in doc1 —not doc2!— such as doc1.groups[2] contains a rectangle. Then spec.isValid is true! This shows that spec is fully reassessed from its symbolic path: /document/group[2]/rectangle[@location=first].

Note. — Contrary to popular belief, it is not necessary that each document hosts 3 groups to set app.documents.everyItem().groups[2] as a container. It suffices that at least one document satisfies this requirement.

From what I have empirically understood, the isValid property of a specifier is TRUE if:
(1) The <CONTAINER> is nonempty (of course!);
   AND
(2) The <SELECTOR> selects at least one actual receiver,
   OR ends by everyItem().

The everyItem exception is proved by the following code:

//-------------------------------------------
// InDesign CS4/CS5
// Test this script with NO OPENED DOCUMENT !
//-------------------------------------------
 
var spec1 = app.documents.itemByRange(0,-1);
var spec2 = app.documents.everyItem();
 
alert( spec1.isValid ); // FALSE
alert( spec2.isValid ); // TRUE
 

Since no document is available, both spec1 and spec2 points out to nothing. However, while spec1 (based on itemByRange) is not valid, spec2 (based on everyItem) is valid. This slight difference, in fact, reflects a pure AppleScript rule: every item is equivalent to items 1 through -1, except that every returns an empty list if there are no elements, instead of an error.” (Apple's Technical Note TN2106, “Scripting Interface Guidelines,” section Object Specifiers) This peculiarity may lead to wrong inferences in a script, so keep in mind that spec.isValid does not always mean that spec has a receiver.

Note 1.isValid is a very special property in that it does not delegate the command to the underlying receiver(s). Like constructor or other fundamental properties of any JS object, isValid remains attached to the specifier itself and cannot result in an Array when you get it from a collective specifier. (See below: “Collective Mode.”) However, retrieving this property causes the resolution of the specifier in case of validity. (See below: “Resolving a Specifier.”)

Note 2. — Before InDesign CS4 there was no direct way to check for the validity of a specifier without error management. The usual workaround was to hit an arbitrary property —like mySpec.id— within a try...catch block. ID CS4 introduced the common isValid property to fix this problem.

Resolving a Specifier

As demonstrated in our first example, a valid specifier can be seen as a pending query whose actual receiver(s) are not identified until we send a command to InDesign. By performing the specifier's query, the system translates any generic path, like /document[0]/story (corresponding to app.documents[0].stories.everyItem()), into a set of absolute paths pointing out to the receiver(s), e.g. /document[@name="Test"]//story[@id=229]. Each resulting specifier is resolved in that it provides a direct access to an UI element identified by its id or name (the name property is used to identify a Document object in CS4 and previous versions). When the original specifier describes multiple receivers, it is resolved to an Array of absolute paths.

A basic way to get resolved specifier(s) from a generic specifier, or path, is to invoke the function resolve(/*string*/path) offered by the JavaScript layer:

//-------------------------------------------
// InDesign CS4/CS5
// Sample uses of the 'resolve' function
//-------------------------------------------
 
// Resolving a specifier:
//=============
var spec = app.documents[0].groups.firstItem().rectangles[1];
var resolvedSpec1 = resolve(spec.toSpecifier());
 
// Here 'resolve' returns a single absolute specifier:
alert( resolvedSpec1.toSpecifier() );
/* E.g.:
/document[@name="Test.indd"]//rectangle[@id=253]
*/
 
 
// Resolving a path:
//=============
var path = '/document[0]/group/rectangle';
var resolvedSpec2 = resolve(path);
 
// Here 'resolve' returns an Array of absolute specifiers:
var i = resolvedSpec2.length;
while( i-- ) resolvedSpec2[i] = resolvedSpec2[i].toSpecifier();
alert( resolvedSpec2.join('\r') );
/* E.g.:
/document[@name="Test.indd"]//rectangle[@id=254]
/document[@name="Test.indd"]//rectangle[@id=253]
/document[@name="Test.indd"]//rectangle[@id=252]
*/
 

The function resolve(...) takes as argument a String —a specifier's path— and returns either a single object specifier, or an Array of object specifiers if the query hits several receivers.

Note that InDesign cannot actually resolve every UI object to an absolute path. For example, the Character objects have no individual id property and are only described by zero-based indexes. Therefore any specifier that handles Text objects (Character, Word, Paragraph, etc.) is resolved to a range of character indexes within a known container:

//--------------------------------------
// InDesign CS4/CS5
// 'Resolving' a Text specifier
//--------------------------------------
 
var spec = app.documents[0].stories.everyItem().words[1];
var resolvedSpec = resolve(spec.toSpecifier());
 
var i = resolvedSpec.length;
while( i-- ) resolvedSpec[i] = resolvedSpec[i].toSpecifier();
alert( resolvedSpec.join('\r') ); // see screenshot below
 

Resolution of the specifier: app.documents[0].stories.everyItem().words[1].

The previous example shows that a resolved specifier doesn't necessary store an invariant entity that you could trust blindly. Indeed, in some cases, it is preferable to work with a generic (dynamic) specifier that you don't resolve too early. Consider myTextFrame.words.firstItem(). The inner path of this unresolved specifier is a context-sensitive link to the first word of myTextFrame. Suppose your script hits the text by adding a new word at the beginning of the story. The generic specifier remains semantically valid while a resolved specifier only provides a snapshot at a given time. This snapshot will no longer be relevant after the modification:

//-------------------------------------------
// InDesign CS4/CS5
// 'Resolved' vs. 'Dynamic' Specifier
//-------------------------------------------
 
var tf = app.activeDocument.textFrames[0],
    firstWord = tf.words.firstItem(); // dynamic spec.
 
// Sets the frame contents (1st word: 'aaaaa')
tf.contents = "aaaaa bbb cc";
 
// Resolves the specifier... too early!
var resolvedWord = resolve(firstWord.toSpecifier());
 
// The contents is then modified:
tf.insertionPoints[0].contents = "zz ";
// The 1st word is then 'zz'
 
alert(
    'Frame contents: ' + tf.contents +
    '\r' +
    '1st word (resolved): ' + resolvedWord.contents +
    '\r' +
    '1st word (dynamic): ' + firstWord.contents
    );
 
/* Displays:
Frame contents: zz aaaaa bbb cc
1st word (resolved): aaaaa
1st word (dynamic): zz
*/
 

The above snippet demonstrates that the resolved specifier is not reliable at the moment the script is querying the word contents. There is another hidden issue: despite the fact that resolvedWord.contents contains the string "aaaaa" (which is the contents of the original first word), we could check that resolvedWord.texts[0].contents is not "aaaaa". . . but "zz aa". Why? Because while the contents property has been retrieved and stored in the object specifier at the exact moment when it was being resolved, the internal path has been updated to reflect the corresponding character range. Thus, resolvedWord is pointing out to the range [0,4] within the characters collection. So when you resolve resolvedWord.texts[0].contents, you send a new command that returns the first five characters of the frame, which now are "zz aaa". In other words, the contents property of the resolved specifier is out of sync with its inner path.

Keep in mind that the visible path of a specifier —spec.toSpecifier()— is never updated in the original object, whatever the way it is resolved. The resolve function is a safe approach as it creates a new specifier, or an array of specifiers, so you can always distinguish the original dynamic specifier with the resolved one(s) and the original object is not affected. But in general, script developers do not use resolve, they directly act from/on the specifier by invoking its properties or methods. Then the specifier is mutely and automatically resolved by the system, which can lead to confusing side effects:

//-------------------------------------------
// InDesign CS4/CS5
// Hidden resolution of a specifier
// Run this script on an empty document
//-------------------------------------------
 
var doc = app.documents[0],
    spec1 = doc.rectangles.everyItem(),
    spec2 = doc.rectangles.everyItem();
 
app.documents[0].rectangles.add(); // 1st rectangle
app.documents[0].rectangles.add(); // 2nd rectangle
 
spec1.isValid; // innocent access to a property...
 
app.documents[0].rectangles.add(); // 3rd rectangle
 
// Checks that spec1 and spec2 have the same path:
alert( spec1.toSpecifier() == spec2.toSpecifier() ); // true
 
// Displays the number of receivers:
alert( spec1.id.length ); // 2 (spec1 was already resolved)
alert( spec2.id.length ); // 3 (spec2 is NOW resolved)
 

In the above example, you could replace spec1.isValid by any other access to the specifier's interface: spec1.label, spec1.move(...), etc. Any command which involves receivers causes an automatic resolution of the specifier. So spec1 will not see the third rectangle: spec1 is already resolved!

A common way to explicitly resolve a specifier and push the resolved data into an Array is to use spec.getElements(). This method performs two operations:
(1) It causes an automatic resolution of spec;
(2) It creates and returns an Array that contains each spec's receiver converted into a resolved specifier.

Moreover, getElements() is much more reliable than any other command because it actually resolves or updates the specifier from its original path:

//-------------------------------------------
// InDesign CS4/CS5
// getElements() used as an updater
// Run this script on an empty document
//-------------------------------------------
 
var doc = app.documents[0],
    spec = doc.rectangles.everyItem();
 
app.documents[0].rectangles.add(); // 1st rectangle
app.documents[0].rectangles.add(); // 2nd rectangle
 
spec.getElements(); // resolves spec
 
app.documents[0].rectangles.add(); // 3rd rectangle
 
alert( spec.id.length ); // 2
 
spec.getElements(); // resolves spec again!
 
alert( spec.id.length ); // 3
 

Of course it is stupid to ask spec.id.length to count the receivers, since spec.getElements().length would much better provide the reliable value! The interesting aspect of the above code is that spec.getElements() updates the specifier even if it has been already resolved, while other properties and methods cannot do so. Think getElements() as a method which rebuilds and resolves any specifier from its original path. In this respect, spec.getElements() is similar to resolve(spec.toSpecifier()), except that getElements() changes the inner state of the object.

Using getElements() as an updater. In blue, the generic path of the specifier. In red, the resolved links at a given time.

Note. — Contrary to resolve(spec.toSpecifier()), spec.getElements() always returns an Array whatever the number of receivers. This array contains one element if the specifier points out to a single receiver, and it can even be empty if no receiver is found.

The ‘Collective Mode’

According to the Adobe scripting help, the pattern ...collection.everyItem() should return an Array of objects having the underlying type. We now know that this is absolutely wrong. An expression like app.documents.everyItem() is a Document specifier, not an Array of Document objects. That said, we have already experienced that querying any property on a collective specifier does not return a single property, it returns a JavaScript Array of properties. For example, app.documents.everyItem().name brings an array of strings, app.documents.everyItem().modified brings an array of booleansapp.documents.everyItem().properties brings an array of objects, etc. It is the same with specifiers based on itemByRange.

Note. — The expressions “collective mode” and “collective specifier” are mine and in no way normative. In other articles, the same concept may be referred to with the adjective “plural” (e.g. “plural specifier”, or “plural class”, in AppleScript documentation).

This magic behavior reflects the fact that a resolved specifier updates its inner state and fits its own interface to work in collective mode if necessary. In this mode, the specifier acts as a delegate that represents all the receivers in a single JavaScript object. Any single property is returned as an array (one item for each receiver), although you can set this property to a simple value (which is then uniformly applied to every receiver). Here are some examples:

//-------------------------------------------
// InDesign CS4/CS5
// Using properties in 'collective mode'
//-------------------------------------------
 
var doc = app.documents[0],
    spec1 = doc.rectangles.everyItem(),
    spec2 = doc.stories.itemByRange(0,2);
 
// Assign 'myLabel' to every rectangle's label:
spec1.label = 'myLabel';
 
// Return an ARRAY of strings
// [ 'myLabel', 'myLabel'... ]
alert( spec1.label );
 
// Assign the same bounds to every rectangle:
spec1.geometricBounds = [0,0,50,50];
 
// Return an ARRAY of arrays
// [ [0,0,50,50], [0,0,50,50]... ]
alert( spec1.geometricBounds );
 
// Assign the same contents to the
// three first stories of the document:
spec2.contents = 'Some text';
 
// Return an ARRAY of strings
// [ 'Some text', 'Some text', 'Some text' ]
alert( spec2.contents );
 
// Ends the collected stories by an exclamation mark
spec2.insertionPoints.lastItem().contents = '!';
 

Well! What about methods? They behave exactly the same way. By calling a method on a collective specifier, you send an ‘all-in-one command’ which performs the same operation on every receiver. If the method is supposed to return a value, then you get an Array of corresponding values. E.g.:

//-------------------------------------------
// InDesign CS4/CS5
// Using duplicate() in 'collective mode'
//-------------------------------------------
 
var doc = app.documents[0],
    spec = doc.rectangles.everyItem();
 
// Duplicates every rectangle
var dups = spec.duplicate(undefined,[10,10]);
 
alert( dups ); // Array of duplicates
 

Note that spec is a collective Rectangle specifier while dups is just an Array of resolved specifiers (the duplicates), so you cannot use dups as a new collective specifier. As far as I know, the Scripting DOM does not offer the ability to regenerate a collective specifier from an array. If you need to perform other operations on dups elements, sorry, you must loop into the array.

An enlightening test is to prototype a custom method within an object and to study its behavior in collective mode. You might think that such a method is invoked N times (one call on each receiver). Nothing of the kind! The method is called once because the this object refers to the (resolved) collective specifier. This confirms the fact that a DOM object is nothing but a specifier, and if necessary a collective one:

//-------------------------------------------
// InDesign CS4/CS5
// Custom method called in collective mode
//
// (Before testing, create several rectangles
// in your document)
//-------------------------------------------
 
Rectangle.prototype.customDuplicate = function()
{
    // In the context,
    // 'this' is a collective specifier:
    alert( this.toSpecifier() );
    /* Displays sth like:
    (/document[@name="Test.indd"]//rectangle[@id=207],
    /document[@name="Test.indd"]//rectangle[@id=206],
    /document[@name="Test.indd"]//rectangle[@id=205])
    */
 
    // ...so r is an Array!
    var r = this.duplicate(undefined,[10,10]);
    alert( r.length );
 
    return r;
}
 
var doc = app.documents[0],
    spec = doc.rectangles.everyItem();
 
var dups = spec.customDuplicate();
 

The code above is pretty convincing! First, it corroborates the fact that spec —i.e. doc.rectangles.everyItem()— is actually treated as a simple Rectangle object: spec.customDuplicate() works perfectly. Secondly, we note that the method is called only once even though spec has several recipients. Finally, in that context, we discover that the this object is encoded as a collective specifier —check out the path string— whose each item is already resolved:
(/document[@name="Test.indd"]//rectangle[@id=207], /document[@name="Test.indd"]//rectangle[@id=206]...).

Tips and tricks

Now that we understand better how DOM objects work under the hood, let's study some useful snippets:

Nested everyItem(). — The collection.everyItem()... syntax can easily aggregate a number of objects in one chained expression. E.g.:

var pPoints = app.
    documents.everyItem().
    rectangles.everyItem().
    paths.everyItem().
    pathPoints[0];
 
// funny effect!
pPoints.anchor = [0,0];
 

Using everyItem().properties. — Like any other, the properties property of an object works like a charm in ‘collective mode.’ So you can set a bundle of uniform properties on multiple objects in one step:

var tableSpec = app.activeDocument.
    stories.everyItem().
    tables.everyItem();
 
tableSpec.properties = {
    topBorderStrokeColor:'Black',
    topBorderStrokeTint: 100,
    topBorderStrokeWeight: '2pt',
 
    bottomBorderStrokeColor:'Black',
    bottomBorderStrokeTint:100,
    bottomBorderStrokeWeight: '2pt',
 
    leftBorderStrokeColor: 'Black',
    leftBorderStrokeTint: 50,
    leftBorderStrokeWeight: '4pt',
 
    rightBorderStrokeColor: 'Black',
    rightBorderStrokeTint: 50,
    rightBorderStrokeWeight: '4pt',
 
    // etc.
    };
 

Using spec.getElements()[0]. — This pattern brings you the first underlying object (as a resolved specifier) behind a generic specifier. It's especially useful when you know that a specifier is only temporarily valid while its receiver will survive. For example, suppose you want to clone the last rectangle of the first page and to put this clone in a new page inserted before the first page:

var pages = app.activeDocument.pages,
    rec = pages[0].rectangles.lastItem();
 
// because pages[0] will change,
// we need to resolve 'rec':
rec = rec.getElements()[0];
 
// insert a new page and
// create a clone of rec:
pages.add(LocationOptions.atBeginning).
    rectangles.add(rec.properties);
 

Note that technically it is not necessary to reset rec to rec.getElements()[0] as long as rec is resolved before the creation of the new page. So we could simply send a command like rec.isValid:

var pages = app.activeDocument.pages,
    rec = pages[0].rectangles.lastItem();
 
if( rec.isValid ) // this resolves 'rec' too!
    {
    pages.add(LocationOptions.atBeginning).
        rectangles.add(rec.properties);
    }
 

Grouped items backup. — A practical use of getElements() is to backup the items of a Group before ungrouping them:

var gp = app.activeDocument.groups[0],
    items = gp.pageItems.everyItem().getElements();
 
gp.ungroup();
// 'gp' is no more valid...
 
// ...but 'items' are safe:
alert( items[0].constructor.name );
 

Creating a collective specifier to manage multiple Spread objects. — This trick only works with a set of page items that belong to the same spread or page. The goal is to manually create a true specifier to handle those objects collectively. Initially you have an Array of page items. The trick is to group these objects temporarily and to resolve group.pageItems.everyItem():

var page = app.activeDocument.pages[0],
    // an arbitrary array of page items:
    myItems = page.rectangles.everyItem().getElements().
        concat(page.ovals.everyItem().getElements()).
        concat(page.textFrames.everyItem().getElements());
 
// Groups the items ('myItems' is still an Array)
var tempGroup = page.groups.add(myItems);
 
// Converts 'myItems' into a collective specifier:
myItems = tempGroup.pageItems.everyItem();
 
// Resolves myItems
myItems.isValid;
 
// Remove the group
tempGroup.ungroup();
 
// NOW 'myItems' is a nice collective specifier:
myItems.fillColor = 'Black';
// etc.
 

Creating your own collection filter. — Contrary to AppleScript, JavaScript does not allow to filter the elements of a collection by using something like the whose keyword. You cannot create smart specifiers in a form like everyItemWhose(/*filters*/), and I don't think it will ever be possible to extend the specification mechanism within a collection. Instead, we can create generic collection methods to encapsulate a filter on everyItem(), provided that we admit to return a resolved object or an Array of resolved objects. Harbs illustrated this point in the Scripting Forum by emulating an itemByLabel function. Here is a more abstract way to create a collection filter:

//--------------------------------------
// Add a generic 'findItems' method
// callable from any collection
//--------------------------------------
Object.prototype.findItems = function(/*obj*/props)
{
    if( !('everyItem' in this) )
        {
        throw new Error("Error: " + this + " is not a collection.");
        }
 
    var ret = this.everyItem().getElements(),
        i = ret.length,
        e, p;
 
    while( i-- && e=ret[i] )
        {
        for( p in props )
            {
            if( (p in e) && e[p]===props[p] ) continue;
            ret.splice(i,1);
            }
        }
    return ret;
};
 
//--------------------------------------
// Sample use
//--------------------------------------
 
// find all text frames having an
// empty label AND overflows==true
var tfs = app.activeDocument.
    textFrames.findItems(
    {
    label: '',
    overflows:true,
    });
 
alert(tfs.length);
 

Feel free to put below your own everyItem() tips...

On ‘everyItem()’ – Part 1