1/ Manipulating Text and Containers
    About Special Characters Code Points and Enumeration
    Manipulating Table Rows and Cell Specifiers
    Dealing with Conditional Texts
    Parent Text Frame of a Cell that overflows
    Grouping Items by Label

2/ Graphics, Colors and Geometry
    Checking whether an object is a Graphic
    Anonymous and/or Unused Swatches
    The None Swatch Case
    Create a Square based on the Diagonal's length
    Scaling Items and Page Uniformly

3/ ScriptUI Tips and Tricks
    Understanding myWidget.onDraw
    Updating Image widgets
    Apply a custom border to an EditText

4/ On the JavaScript/ExtendScript Side
    The Perils of JS Rounding Methods
    Extract all Parts (sub-arrays) of a given Array
    Mimicking Object Binding in ExtendScript
    How to pass a Script to the Main Engine (from a persistent one)


1/ Manipulating Text and Text Containers

About Special Characters Code Points and Enumeration

Given a Character myChar whose contents property returns a SpecialCharacter enumerator, you can still retrieve the Unicode codepoint using myChar.texts[0].contents instead of myChar.contents:

// Display the Unicode CP in hexadecimal form
alert( myChar.texts[0].contents.charCodeAt(0).toString(16) );
 

Technical Note. — Since InDesign CS5 the Enumeration objects (Locale, SpecialCharacters, etc.) describe a set of Enumerator objects rather than a set of numbers. An Enumerator is a special native object which converts itself into either a String or a Number depending on the context:

// ID CS5+
alert( SpecialCharacters.emDash );  // => EM_DASH (string)
alert( +SpecialCharacters.emDash ); // => 1397058884 (number)
 

In fact, these respectively correspond to myEnumerator.toString() and myEnumerator.valueOf():

// ID CS5+
 
var myEnumerator = SpecialCharacters.emDash;
 
alert( myEnumerator.toString() ); // => EM_DASH (string)
alert( myEnumerator.valueOf() );  // => 1397058884 (number)
 

In addition, every Enumerator has its == and === operators overriden so that we can still compare it with a number:

alert( SpecialCharacters.emDash==1397058884 );  // => true
alert( SpecialCharacters.emDash===1397058884 ); // => true
 

Also, an Enumeration object see both the camelcase and the uppercase name of an enumerator as a regular own property:

// ID CS5+
alert( SpecialCharacters.hasOwnProperty('emDash') );  // => true
alert( SpecialCharacters.hasOwnProperty('EM_DASH') ); // => true
 

Both are [[enumerable]] although the for-in loop only collects the uppercase forms ('EM_DASH', etc.).

• Original discussion: http://forums.adobe.com/message/4153310#4153310

• See also: “InDesign Special Characters”

Manipulating Table Rows and Cell Specifiers

Given a Table object myTable that only contains 3 rows, the following code surprisingly adds 20 rows to the entire table:

var myRow = myTable.rows[1];
 
// This 4-passes loop will add 20 rows! Why?
// ---
for( var i=0; i < 4 ; ++i )
    {
    myTable.rows.add(LocationOptions.AFTER, myRow);
    }
 

The secret is that myRow is not a row! It's nothing but a path in the object hierarchy (a specifier). We may see myRow as a basic structure that simply stores an address, such as:

    "/document[@id=1]//text-frame[2]/table[5]/row[1]"

The actual path depends on the way the variable is defined. In our example, the main part of the path is already resolved when we send the very first command to the specifier because the subsystem needs to access the textframe and the table (which are persistent components). So, probably myRow looks more like this:

    "/document[@id=1]//text-frame[@id=217]/table[@id=228]/row[1]"

Well. This path seems quite stable at first sight. But we already know that rows are not persistent components (they have no ID.) Under the hood, tables only contain cell ranges. Rows are pure abstractions as well as words, lines, or paragraphs regarding text streams. Indeed, myTable.rows[1] is very similar to myStory.words[1]: the range or indices these specifiers point out to highly depend on the current state of the parent object (table or story).

Now, the first time we send a command to myRow (i.e., the first time we use myRow in the script), the specifier is internally resolved—if valid. In other words, a specific location (range, indices, pointers, whatever) is determined within the target table and attached to myRow. This is a subsystem thing. We cannot ‘unresolve’ myRow in itself, but of course we could simply affect to myRow a new unresolved specifier—myRow = myTable.rows[1]—and then wait for the right time to send a new command!

Meanwhile, something is happening to myTable: myTable.rows.add(LocationOptions.AFTER, myRow);

One might assume that this has no effect on the inner location attached to myRow, due to LocationOptions.AFTER, but since we have no idea of how the cells are actually handled and managed by the subsystem, this is a weak assumption! Note that it's easy to get similar issues with text ranges:

var myChar = myStory.characters[1];
 
myChar.duplicate(LocationOptions.AFTER, myChar);
 
alert( myChar.contents ); // => 2 characters!!!
 

To conclude, the inner location attached to myRow is no more what we perceive as myTable.row[1]. From now, if we still need to access to myTable.row[1], then we have to:

(a) Explicitly use myTable.row[1] (which by nature forces a resolution)

OR

(b) Find a way to re-resolve myRow.

…which is just the purpose of the getElements() method. What makes getElements() more reliable than other methods is that it actually updates the specifier from its original path, so it remembers that myRow is (was) defined as myTable.row[1], in fact:

    "/document[@id=1]//text-frame[@id=217]/table[@id=228]/row[1]"

That's why myRow.getElements() resets myRow to what it really means and solves the problem.

• Original discussion: http://forums.adobe.com/message/4475351#4475351

Dealing with Conditional Texts

Since multiple conditions can be applied to the same piece of text, the property myText.appliedConditions returns an array. This array may be empty if no condition is applied to the text. Hence, to prevent runtime errors, don't forget to check the array's length.

Suggested code:

var conds = myText.appliedConditions;
var firstCondName = conds.length ?
    conds[0].name :
    '[No Condition]';
 

• Original discussion: http://forums.adobe.com/message/3836841#3836841

Parent Text Frame of a Cell that overflows

The InDesign Scripting DOM does not seem to offer any robust access to the parent text frame of a Cell object (provided that it actually belongs to such component). In the case the Cell entirely overflows—i.e. myCell.insertionPoints[0].parentTextFrames.length===0—I don't know any mean to get the actual parent frame. The only approach I see is to parse the parentRow of the targeted cell and to look whether any other cell in that row can access to a parent frame, provided that all cells of a specific row should share the same parent frame. This workaround will not work if every cell of the row entirely overflows!

Suggested code:

function getCellFrame(/*Cell*/c)
// -------------------------------------
// Try to find the TextFrame that 'contains' c (if any)
// Return the TextFrame if found, otherwise:
// => NULL if the tf cannot be determined
// => FALSE if sth goes wrong
{
    var a, i, t;
 
    if( !(c instanceof Cell) )
        return false;
 
    if( (a=c.insertionPoints[0].parentTextFrames).length )
        return a[0];
 
    a = c.parentRow.cells.everyItem().
        insertionPoints[0].parentTextFrames;
    i = a.length;
    while( i-- ){ if( t=a[i][0] ) break; }
 
    return t || null;
}
 

• Original discussion: http://forums.adobe.com/message/4439122#4439122

Grouping Items by Label

“I search to group items in document with the same script label.”

Preliminary notice. — Since a group cannot extend over a spread we have to build groups spread by spread.

Suggested code:

// =====================================
// Group labelled items (spread by spread)
// =====================================
 
var LABEL_PREFIX = "Enter here your specific label prefix";
 
var doc = app.documents.length && app.activeDocument,
    spreads = doc && doc.spreads,
    s = spreads ? spreads.length : 0,
    items, a=[], t, i, z=0;
 
while( s-- )
    {
    items = spreads[s].pageItems.everyItem().getElements();
    i = items.length;
    z = 0;
    while( i-- )
        {
        if( 0 != (t=items[i]).label.indexOf(LABEL_PREFIX) ) continue;
        a[z++] = t;
        }
    z && spreads[s].groups.add(a);
    a.length = 0;
    }
 

• Original discussion: http://forums.adobe.com/message/4324230#4324230

2/ Graphics, Colors and Geometry

Checking whether an object is a Graphic

“I would like to determine whether an item might be either an instance of the Graphic class or be a subclass—provided that Graphic contains EPS, Image, ImportedPage, PDF, PICT, and WMF. Unfortunately, Graphic.isPrototypeOf(myObj) does not work.”

What the DOM claims to be a “base class” or a subclass does not exactly meet the JS rules regarding instanceof or isPrototypeOf due to the specific nature of object specifiers and the way they are resolved. To my mind, PageItem or Graphic should be regarded as virtual classes for temporary specifiers that are not resolved yet. Once a specifier is resolved—which is automatically done through array properties like allPageItems or allGraphics—it simply takes its own type and there is no more connection with the virtual base class. Therefore, to find whether a resolved object is a kind of Graphic, a practical approach is to check whether it addresses a specific property only shared by graphics, e.g.:

if( myObj.properties.hasOwnProperty('imageTypeName') )
    {
    // myObj is a Graphic...
    }
 

Then, of course, we can use switch(myObj.__class__){ ... } or switch(myObj.constructor){ ... } to refine treatments.

• Original discussion: http://forums.adobe.com/message/4461264#4461264

Anonymous and/or Unused Swatches

What is really fun with InDesign swatches is that, depending on how the user manipulates the document, it may sometimes verify the following assertion:

    doc.swatches.length < doc.unusedSwatches.length

which at first glance seems absurd.

In fact, the concept of swatch has two different scopes according to the context!

(1) A Swatch1 (primary meaning) usually refers to an “abstract class” that addresses as well: colors, tints, gradients, mixed inks, mixed ink groups, and even a special instance, the [None] swatch (the only element whose actual cast is Swatch).

(2) A Swatch2 (secondary meaning) refers to a named Swatch1, i.e. an element that is shown in the Swatches panel (and whose name property is defined).

The doc.swatches collection only sees Swatch2 elements, while the doc.unusedSwatches array may also contain Swatch1 elements, i.e. anonymous colors or tints that are not used in the layout but still exist behind the scene.

To my knowledge, an anonymous Swatch1 can only be of type Color or Gradient. Each time the user plays with the sliders in the Colors panel or in the Gradients panel, I think InDesign mutely creates the corresponding anonymous Swatch1 elements, which increases the unusedSwatches count!

Note that unused swatches and anonymous swatches are two really different things. An anonymous Swatch1 may be in use (e.g. the user locally applies an unnamed color) and conversely a strict named Swatch2 may be unused (as shown by the “Select All Unused” feature).

InDesign Swatches Chart

A serious limitation of the DOM is that we have no direct access to all anonymous swatches as such.

It is possible to investigate on the anonymous swatches in use through:

// only target anonymous swatches IN USE
app.menuActions.itemByName("$ID/Add All Unnamed Colors").invoke();
 

but the obscure swatches which are both anonymous and unused won't appear. Therefore it is quite difficult to analyze in depth these elements via a script.

• Original discussion: http://forums.adobe.com/message/4506745#4506745

The [None] Swatch Case

Whatever the locale the None swatch has always the internal name "None"—even if it is localized in the Swatch panel as [None], [Sans], [Ohne], etc. depending on the locale. This fact is very specific to the Swatches collection, which basically uses None, Black, Paper and Registration as unique reserved names in a locale-independent way. (By contrast, the reserved names of the styles collection, including None, are locale-dependent!)

As a consequence, the regular way to point out to the None swatch in any language is:

var noneSwatch = myDoc.swatches.itemByName('None');
 
alert( noneSwatch.isValid ); // should always be true
alert( noneSwatch.name );    // should always be"None"
 

Likewise, the shortcut to apply this swatch—in any language—still is:

// Apply the [None] swatch as a fill color
 
myObj.fillColor = "None";
 

Note that myObj.fillColor = "$ID/kNoneName" will fail in non-EN locales because this internally involves app.translateKeyString("$ID/kNoneName") which results in: "Ohne" (German), "Sans" (French), etc., and then does not match the required reserved name.

By contrast, name-based access to special object styles can be done locale-dependently, and should be done locale-independently:

// Recommended way to access the [None] style
var noneStyle = myDoc.objectStyles.itemByName('$ID/[None]');
 

• Original discussion: http://forums.adobe.com/message/4457638#4457638

Create a Square based on the Diagonal's length

We can easily retrieve the height and the width of a page, then calculate the diagonal, then create a Rectangle based on that length. The following snippet shows how we can make such routine work in any context, supporting custom measurement units, custom rulers settings, rotated spreads and/or even scaled/skewed pages! To do so we must avoid usual methods based on “geometric bounds.” This is a good example of working with InDesign coordinate spaces and transformations.

Suggested code:

// ===========================
// Create a square based on the active
// page's diagonal length (10%)
// ===========================
 
function measureDiagonal(/*Page*/page)
// -------------------------------------
// Ret. the page's diagonal in pts (relative to the page CS)
{
    var CS_INNER = CoordinateSpaces.innerCoordinates;
 
    var wh = page.resolve(AnchorPoint.bottomRightAnchor, CS_INNER)[0],
        w = wh[0],
        h = wh[1];
 
    return Math.sqrt(w*w + h*h); // Pythagorean theorem
}
 
function createTopLeftCornerRectangle(/*Page*/page, /*num[2]*/wh)
// -------------------------------------
// wh: width and height of the rectangle in pts (relative to the page CS)
{
    // Some const shortcuts
    // ---
    var CS_SPREAD = CoordinateSpaces.spreadCoordinates,
        CS_INNER = CoordinateSpaces.innerCoordinates,
        RM_REPLACE = ResizeMethods.replacingCurrentDimensionsWith,
        AP_TOP_LEFT = AnchorPoint.topLeftAnchor;
 
    var spread = page.parent,
        // Create a rectangle (in the spread CS--the page CS is not relevant yet)
        // ---
        rec = spread.rectangles.add({fillColor:'Black'}),
        // Page transformation values (relative to the spread)
        // ---
        pageMxValues = page.transformValuesOf(CS_SPREAD)[0].matrixValues;
 
 
    // Normalize the rectangle in the spread
    // (the size does not matter here)
    // ---
    rec.reframe(CS_SPREAD, [[0,0],[10,10]]);
 
    // Apply the page transfo to the rectangle
    // so that its inner space fits the page space
    // ---
    rec.transform(CS_SPREAD, [[0,0], CS_SPREAD], pageMxValues);
 
    // Finally, resize the rec
    // ---
    rec.resize(CS_INNER, AP_TOP_LEFT, RM_REPLACE, wh.concat(CS_INNER));
 
    return rec;
}
 
 
var FACTOR = .1, // 10%
    win = app.layoutWindows.length && app.activeWindow,
    page = win && (win instanceof LayoutWindow) && win.activePage,
    size = page && page.isValid && FACTOR*measureDiagonal(page);
 
 
size && createTopLeftCornerRectangle(page, [size,size]);
 

• Original discussion: http://forums.adobe.com/message/4318994#4318994

Scaling Items and Page Uniformly

A general problem in transforming page items in an uniform way is to deal with the tranformation origin. E.g. when an object is scaled, the origin of the transformation matters in that it determines how that object will move. So, when we want to scale two objects while preserving their relative location, we have to use the same origin. Since the origin of a transformation is not saved within the TransformationMatrix, we need to explicitly provide the transformation origin each time that matrix is applied.

In the transform method, the origin is the second argument. There are many way to specify that location. What we need to know is that AnchorPoint.CENTER_ANCHOR in myItem.transform(...) and in myPage.transform(...) does not refer to the same location (unless by chance the center point of the object fits the center point of the page).

If the transformation must be applied from the perspective of the page center, the following code template can help:

function myTransform(myPage, myItem, myScaleMatrix)
{
    var everything = myItem.everyItem(),
        origin = myPage.resolve(AnchorPoint.CENTER_ANCHOR,
            CoordinateSpaces.PASTEBOARD_COORDINATES)[0];
 
    everything.transform(
        CoordinateSpaces.PASTEBOARD_COORDINATES,
        origin, // absolute coord. in the pasteboard space
        myScaleMatrix);
 
    myPage.transform(
        CoordinateSpaces.PASTEBOARD_COORDINATES,
        origin, // absolute coord. in the pasteboard space
        myScaleMatrix);
}
 

Technical note. — When applying scaling transformations to pages, make sure app.transformPreferences.whenScaling is set to WhenScalingOptions.APPLY_TO_CONTENT, otherwise page scaling will not actually change the dimensions of the page. When app.transformPreferences.whenScaling is set to WhenScalingOptions.ADJUST_SCALING_PERCENTAGE, any scaling applied to a page only transforms the view in the app interface, but the exported page has its original size preserved. Note that in both cases, transforming a page has no visible effect on the underlying page items, but this makes a crucial difference in the exported document as the PDF is based on the actual size of the page(s).

• Original discussion: http://forums.adobe.com/message/4179133#4179133

• See also: “Resize/transform pageitem – centered on Page/Spread”

3/ ScriptUI Tips and Tricks

Understanding myWidget.onDraw

As documented, the onDraw callback of a ScriptUI widget is automatically called when the widget needs to be (re)drawn, but this 'when' is highly unpredictable as it depends on the user interactions, the OS, and probably a number of low-level events. We also know that custom layout—i.e. myContainer.layout.layout(1)—triggers such drawing stages from containers.

The issue with manually calling a custom myWidget.onDraw() method is we generally don't know when it is safe to call it and whether it may conflict with the automatic calls. There are circumstances where manual onDraw() simply fails due to a temporary invalidation of the widget.graphics property (I believe this depends on the object type).

However, onDraw is a simple (callable) function if the script did it so. What matters is that myWidget.onDraw() overrides and bypasses, as much as possible, the native drawing stage. So, in many cases, an empty onDraw() callback makes the widget totally undrawn (hidden) although room has been reserved to it during layout. (That's not true for composite or very interactive widgets such as ScrollBar or ListBox which still inherit from low-level painting events.)

When you need to produce the default widget skin before further customization, a usual approach is to first invoke the ScriptUIGraphics.drawOSControl() method. Then you can adjust the skin of your widget. Here are two examples:

// =====================================
// EditText placeholder
// =====================================
 
var u,
    w = new Window('dialog', "Display Placeholder"),
    e1 = w.add('edittext'),
    e2 = w.add('edittext'),
    b = w.add('button', u, "OK"),
    // ---
    wgx = w.graphics,
    grayPen = wgx.newPen(wgx.PenType.SOLID_COLOR,
        [.67,.67,.67], 1);
 
e1.characters = e2.characters = 32;
 
e1.onDraw = e2.onDraw = function(/*DrawState*/)
{
    var gx = this.graphics;
    gx.drawOSControl();
    this.text || this.active ||
        gx.drawString("<required>", grayPen, 0, 0);
};
 
w.show();
 

And:

// =====================================
// Underlined StaticText
// =====================================
var u,
    w = new Window('dialog', "Underline Me!"),
    s1 = w.add('statictext', u, "Not Underlined"),
    s2 = w.add('statictext', u, "Underlined"),
    b = w.add('button', u, "OK"),
    // ---
    wgx = w.graphics,
    linePen = wgx.newPen(wgx.PenType.SOLID_COLOR,
        [0,0,0], 1);
 
s2.preferredSize[1] += 3;
 
s2.onDraw = function(/*DrawState*/)
{
    var gx = this.graphics,
        sz = this.preferredSize,
        y = sz[1]-1;
 
    gx.drawOSControl();
    gx.newPath();
    gx.moveTo(0, y);
    gx.lineTo(sz[0],y);
    gx.strokePath(linePen);
};
 
w.show();
 

• Original discussion: http://forums.adobe.com/message/4215908#4215908

Updating Image widgets

Image widgets are just containers. In usual situations, when I need to update the picture within a container, I keep the widget in place and I only update its .image property with another ScriptUIImage instance. So I don't remove any ScriptUI widget. Let's illustrate this with a stupid sample code:

var u,
    w = new Window('dialog'),
    p = w.add('panel'),
    img1 = p.add('image', u, "path/to/test.png"),
    img2 = p.add('image'), // empty container, size=[0,0]
    b = w.add('button', u, "Test");
 
b.onClick = function()
{
    img2.image = img1.image;
    img2.size = img1.size;
    w.layout.layout(1);
};
 
w.show();
 

Note that img1 and img2 are two distinct Image containers that already exist when the dialog show up. The onClick event handler just loads img1.image into img2.image, which actually duplicates the link into the existing ScriptUIImage instance. Then I resize the container accordingly and update the layout. A similar approach could be used if we only had to dynamically change/switch image contents within a set of fixed Image containers.

• Original discussion: http://forums.adobe.com/message/4297972#4297972

• See also: “Sprite Buttons in ScriptUI”

Apply a custom border to an EditText

On Mac OS, the EditText widget is subject to a specific focus ring wich automatically allocates 2 or 3 additional transparent pixels all around the field. Hence, if the control is nested within a background-colored group, that color is revealed through this additional space. From that point, drawing a custom border leads to apply two fixes:

1. We first create a white subgroup between the outer group and the EditText to make sure that the revealed background fits the EditText background.

2. We then make the EditText borderless to inhibit its own stroke.

Suggested code:

// ========================== 
// ScriptUI EditText with
// custom border (Win & Mac)
// ========================== 
 
var STROKE_COLOR = [1,0,0], // red
    STROKE_WEIGHT = 1,      // in pixels
    FILL_COLOR = [1,1,1],   // white
    INSET_SPACE = 3;        // in pixels -- Mac OS needs it >=3
 
var u,
    w = new Window('dialog'),
    // ---
    gBorder = w.add('panel').add('group'),
    gSpacer = gBorder.add('group'),
    e = gSpacer.add('edittext', u, u, {borderless:true}),
    // ---
    bCancel = w.add('button', u, "Cancel"),
    // ---
    gx = w.graphics,
    SOLID_BRUSH = gx.BrushType.SOLID_COLOR;
 
// Edit field settings
// ---
e.characters = 12;
gBorder.margins = STROKE_WEIGHT;
gBorder.graphics.backgroundColor = gx.
    newBrush(SOLID_BRUSH, STROKE_COLOR );
gSpacer.margins = INSET_SPACE;
gSpacer.graphics.backgroundColor = e.graphics.
    backgroundColor = gx.newBrush(SOLID_BRUSH, FILL_COLOR);
 
w.show();
 

• Original discussion: http://forums.adobe.com/message/4318873#4318873

4/ On the JavaScript/ExtendScript Side

The Perils of JS Rounding Methods

Be very careful when you manipulate JavaScript rounding methods such as Number.toFixed()—especially if you are treating currency values that require correct results! Since JS floating point numbers are considered “only as reliable as possible and no more,” rounding methods are highly non-predictable. For example,

    (1.255).toFixed(2) may return 1.25

whereas

    (1.355).toFixed(2) may return 1.36

I wrote ‘may’ because this also depends on the OS: (19.125).toFixed(2) returns 19.12 on Mac Lion (64 bits) while the same code returns 19.13 on Win XP (32 bits)—all tested with ID CS5.5.

• Original discussion: http://forums.adobe.com/message/4402568#4402568

Extract all Parts (sub-arrays) of a given Array

Suggested code:

function arrayParts(/*arr*/a, r, i, j, t, s, p)
// -------------------------------------
// Note: a is supposed to contain unique items
// [if necessary, apply a makeUnique routine first]
{
    if(! (i = Math.pow(2,a.length)-1) ){ return null; }
    (r=[]).toString = function(){return this.join('\r');};
 
    while(i--)
        {
        r[i] = t = [];
        s = (1+i).toString(2);
        p = (j=s.length) - 1;
        while( j-- ) '1'==s[j] && t[t.length]=a[p-j];
        }
 
    // Reorder by length [if needed!]
    // ---
    r.sort( function(x,y){return x.length-y.length;} );
 
    return  r;
}
 
// Sample code
// ---
var arrTest = ['Blue','Red','Yellow','Green','Black'];
alert( arrayParts(arrTest) );
 

• Original discussion: http://forums.adobe.com/message/4501787#4501787

Mimicking Object Binding in ExtendScript

ExtendScript provides a Object.watch() method that leads to pretty weird experimentations. In the following code, we emulate a kind of dynamic length property at the object level to address the length of an inner array:

// My original object
// ---
var myObj = {
    items: [],
    meth: function(){ alert("Hello World!"); }
    };
 
// Adding length as a 'fake property' (getter and setter)
// ---
(myObj.length = function F(n)
{
    if( !F.root )
        {
        (F.root=this).watch('length', function(_,ov,nv)
            { F(nv); return ov; });
        F.valueOf = function()
            { return F().valueOf(); };
        F.toString = function()
            { return F().toString(); };
        }
 
    if( 'undefined' == typeof n )
        n = F.root.items.length >>> 0;
    else
        F.root.items.length = n;
    return n;
}).call(myObj);
 
 
// Usage
// ---
myObj.meth(); // => Hello World!
myObj.items[0] = 10;
myObj.items[1] = 20;
alert( myObj.length ); // => 2
 
var x = myObj.length-1; // works too, thanks to F.valueOf
alert( x ); // => 1
 
myObj.length = 5; // testing the setter
alert( myObj.length ); // => 5
alert( myObj.items ); // => 10,20,,,
 

• Original discussion: http://forums.adobe.com/message/4250385#4250385

How to pass a Script to the Main Engine (from a persistent one)

Suppose a part of your code must be run into the main targetengine while you are using a session engine (in order to manage a ScriptUI Palette, for example). A possible approach is to embed the desired code as a string within a single app.doScript request that also includes a #targetengine directive.

Suggested code:

#targetengine "mySession"
 
w = new Window('window', undefined, undefined,
    {resizeable:true, borderless:false}
    );
 
myPanel = w.add('panel');
w.add('statictext', undefined, "main");
b = w.add('button', undefined, "Press here!");
 
b.onClick = function()
    {
    app.doScript("#targetengine 'main'\r"+test.toSource()+"();",
    ScriptLanguage.javascript, undefined, UndoModes.entireScript);
    }
 
w.show();
 
function test(){ alert($.engineName); }
 

Although the test is unknown from the main engine, we pass its entire body to doScript thanks to Function.toSource().

• Original discussion: http://forums.adobe.com/message/4179231#4179231,


See also: “Scripting Forum Roundups”,