The JavaScript DOM attaches to the Page object a move method specified as follows:

Page.move (to: LocationOptions, reference: Page|Spread, binding: BindingOptions): Page

where:

to is a LocationsOptions enumerator (among .AFTER, .BEFORE, .AT_BEGINNING, .AT_END, .UNKNOWN) indicating “the new location of the page relative to the reference object or within the document or spread”,

reference is the Page or Spread object in relation to the previous argument when specified by LocationsOptions.AFTER or LocationsOptions.BEFORE,

binding specifies (if needed) “the location of the binding spine in spreads”: BindingOptions.LEFT_ALIGN, BindingOptions.RIGHT_ALIGN or BindingOptions.DEFAULT_VALUE.

There's no difficulty in invoking repeatedly the Page.move() method except that you need to care about the page reference changes during the process. Suppose you want to swap page 4 and page 7. An intuitive way to do this is to move page 7 after page 4, then to move page 4 after page 7 . . . Hey, wait a minute! Page 7 is no more the 7th page after the first step:

Swapping two pages within a multipage document.

So we must recalculate the page reference.

How to get a page reference

When querying a Page object from a Pages collection, it's very important to make a distinction between the “page name” and the “page number”. In fact, the page number is an illusion. Because of the contingent section prefix, JavaScript handles the Page.name property as a String. Thus, assuming no section prefix is applied, the good way to get a page from its ‘number’ is to convert the Number into a String before invoking explicitly the Pages.itemByName() method. Example: myPage = myDoc.pages.itemByName(''+myNumber).

On the other hand, any page has a documentOffset property which is the zero-based index of the page within the document. But there are two pitfalls to avoid: confusing Page.index and Page.documentOffset; and querying the Pages getters —like Pages[] and Pages.item()— without consideration for implicit type conversions. Since the Page.index property only returns the page index within its spread container, it's often useless when you target the document. To understand the second fact, let's compare various access methods:

var pages = app.activeDocument.pages;
 
alert( pages[3].name ); // displays: 4
alert( pages["3"].name ); // displays: 4
 
alert( pages.item(3).name ); // displays: 4
alert( pages.item("3").name ); // displays: 3
 
alert( pages.itemByName(3).name ); // ERROR
alert( pages.itemByName("3").name ); // displays: 3
 

We can conclude that:

• The square bracket operator Pages[] always expects the page zero-based index as a Number, and will implicitly convert anything else into a Number.

• The Pages.item() method expects either a zero-based index (Number) or the page name (String), so the result depends on the type you provide.

• The Pages.itemByName() method expects the page name (String) and does not perform type conversion.

Anyway, you need to keep in mind that Number(myPage.name) is not necessary equal to myPage.documentOffset+1 regarding the Section.pageNumberStart option.

Also, because of the Spread.allowPageShuffle, DocumentPreference.allowPageShuffle and DocumentPreference.preserveLayoutWhenShuffling constraints, Page.move() can underperform when used between gatefold spreads. (In the next section, it's supposed we are working with facing-page or single-sided multipage documents.)

Swapping two pages

Here's a very simple code to perform page swapping:

function swapPages(p0, p1)
{
if (p0 == p1) return true;
 
var pages = app.activeDocument.pages;
 
var pg0 = pages.itemByName(''+p0); // force string argument
var pg1 = pages.itemByName(''+p1); // force string argument
 
if ( pg0.documentOffset > pg1.documentOffset ) return swapPages(p1,p0);
 
try {
  pg1.move(LocationOptions.AFTER,pg0);
  pg0.move(LocationOptions.AFTER,pages.itemByName(''+p1));
  return true;
  }
catch(ex) {return false;}
}
 
// usage:
swapPages(4,7);
 

The swapPages() function expects two page numbers —in fact, two page names— given as String or Number. The implementation uses the documentOffset property only to check the initial page order.

Reordering all the pages of a document

The following script (for ID CS4) is more ambitious. It provides an interface allowing the user to rearrange the whole document by simply inputting a ‘reordering’ string. For example, 4, 7-5, 8, 1-3 remaps a 8-page document according to the sequence: 4, 7, 6, 5, 8, 1, 2, 3.

function reorderPages(/*str*/newOrder)
//--------------------------------------
{
var pages = app.activeDocument.pages;
 
var pgMap = (function()
    {
    var items = newOrder.replace(/[^\d,-]/g,'').split(',');
    var r = [], bkp={}, i, i0, i1, sz, p, inc;
 
    while(i=items.shift())
        {
        i = i.split('-');
 
        // don't allow "x-y-z"
        if ( (sz=i.length) > 2 ) return "Invalid range: '"+i.join('-')+"'";
 
        i0 = Number(i[0]);
        i1 = Number(i[sz-1]);
        inc = ( i0 > i1 ) ? -1 : 1;
 
        for( p=i0 ; p*inc <= i1*inc ; p+=inc )
            {
            if (!(pages.item(''+p).isValid))
                return "The page "+p+" doesn't exist!";
            if ((''+p) in bkp)
                return "The page "+p+" is specified twice!";
            bkp[''+p]=1;
            r.push(pages.item(''+p).id);
            }
        }
    return r;
    })();
 
if ( typeof pgMap == 'string') return pgMap;
 
var sz = pgMap.length;
if (sz != pages.length)
    return "Page number mismatch -- "+pgMap.length+" given, "+pages.length+" in document";
 
// at this point, {pgMap} contains a valid permutation
 
app.scriptPreferences.enableRedraw = false;
for(var i=0 ; i < sz ; i++ )
    {
    pages.itemByID(pgMap[i]).move(LocationOptions.AT_END);
    }
app.scriptPreferences.enableRedraw = true;
 
return true;
}
 
function main()
//--------------------------------------
{
if (!app.documents.length)
    {alert("Please open a document");return;}
 
if (app.activeDocument.pages.length < 2)
    {alert("The document has a single page!");return;}
 
var order = (function()
    {
    return this.lastItem().name + '-' + this.firstItem().name;
    }).call(app.activeDocument.pages);
 
var ret;
while(true)
    {
    order = prompt("Enter the new order of the pages using page numbers and/or page ranges separated by commas (e.g. 4, 7-5, 8, 1-3):",
        order, " Reorder Pages  |  \u00A9Indiscripts.com");
    if (order === null) return;
    ret = reorderPages(order);
    if( ret === true ) return;
    alert(ret);
    }
}
 
main();
 

The reorderPages() function exploits an interesting fact: when moving a page around the document, the internal id of the page —Page.id— remains invariant. Thus, storing the page ids in an array before using the function provides permanent object references.

The script above has been tested for a long document (200+ pages) and should still provide good performances for more pages. We explored a few variants in the InDesign Scripting Forum.