Google forms, and reordering pages

I had to design a somewhat large online suvery in Google forms, and was missing a way to move pages around. It seems this functionality is not part of the system. And, even though you can control page order by linking them, this wrecks havoc with the progress bar, that is only calculated by page number, not order. So, I wrote this Google App Script to fix it. It is rather crude, and I do not understand the project model, and what not, but you can probably google that part.

Also, I only needed to move pages forward. If you need to move pages backward, you will either have to write that code, or move all other pages forward. Use the “MovePages” function to control it, run it from the toolbar.

Here goes:


// Code to reorder pages, and get some information when working with many pages.
// by Mads Bondo Dydensborg <mads@dydensborg.dk>


// Page handling in forms are weird. A page consists of a pagebreak and a number of items.
// Expect the first page, that are only items, and you can not change the first two items, which
// are not even part of the items array.

// Call this to move a number of pages.
function MovePages() {
// movePageForward( 28, 17 );
for ( var i = 30; i <= 37; ++i ) {
// movePageForward( i, i - 9 );
}
}

// Helper/info function
// Show alert box with all titles of all pages
function listTitles() {
var firstItem = FormApp.getActiveForm().getItems()[0];

var titles = "Page 1 : 'Your Title'\n";
var count = 1;
var titles = titles + getAllPageItems().map(
function(item) {
++count;
return "Page " + count + " : " + item.getTitle() + "";
}).join("\n");
FormApp.getUi().alert("List of pages", titles, FormApp.getUi().ButtonSet.OK );
}


/////////////////////////////////////////////////////////////////////////////
// These numbers are based on the users perspective.
// The first page is called "1", and does not usually have a page_break to begin with!
// This method moves a page forward,
function movePageForward(pageNumber, toBeforePageNumber) {
// Check bounds
checkPageBounds(pageNumber);
checkPageBounds(toBeforePageNumber);
// Check ordering, as this moves forward
if ( pageNumber <= toBeforePageNumber ) {
throw new Error( "This method can only move pages forward" );
}
Logger.log( "Moving page " + pageNumber + " to before page " + toBeforePageNumber );
// Forward approach: Identify item index number to move to
// Collect all item instances
// Ask the form to move them
var beforeIndex = getPageItem(toBeforePageNumber).getIndex();
// FormApp.getUi().alert( "Moving to index : " + beforeIndex );
Logger.log( "Moving page " + pageNumber + " to index " + beforeIndex );
// Collect items
var movingPageItems = getPageItems(pageNumber);
// Move them all!
var form = FormApp.getActiveForm();
for( i = 0; i < movingPageItems.length; ++i ) {
Logger.log( "Moving item with global index : " + movingPageItems[i].getIndex() + " to index : " + (beforeIndex + i) );
form.moveItem( movingPageItems[i], beforeIndex + i);
}
}

/////////////////////////////////////////////////////////////////////////////
// Get all items related to a given page.
function getPageItems(thisPageNum) {
Logger.log("Getting items for page number: " + thisPageNum );
checkPageBounds(thisPageNum);
var thisPageItems = []; // Used for result
var thisPageBreakIndex = getPageItem(thisPageNum).getIndex();
Logger.log( "This is index num : " + thisPageBreakIndex );
// Iterate from this, until we meet a PAGE index type
// Get all items
var form = FormApp.getActiveForm();
var allItems = form.getItems();
thisPageItems.push(allItems[thisPageBreakIndex]);
Logger.log( "Added pagebreak item: " + allItems[thisPageBreakIndex].getIndex() );
for( var i = thisPageBreakIndex+1; ( i < allItems.length ) && ( allItems[i].getType() != FormApp.ItemType.PAGE_BREAK ); ++i ) {
thisPageItems.push(allItems[i]);
Logger.log( "Added non-pagebreak item: " + allItems[i].getIndex() );
}
return thisPageItems;
}


/////////////////////////////////////////////////////////////////////////////
// Get all the pagebreak items
function getAllPageItems() {
// Get the form
var form = FormApp.getActiveForm();
// All the pagebreak items
var pageItems = form.getItems(FormApp.ItemType.PAGE_BREAK);
return pageItems;
}



/////////////////////////////////////////////////////////////////////////////
// Get the max page number in user numbering
function getMaxPageNumber() {
var pageItems = getAllPageItems();
// Number of pageBreaks + 1, is user number (2 pb => 3 pages => 3 is max number)
return pageItems.length + 1;
}

/////////////////////////////////////////////////////////////////////////////
// Check bounds, throw if outside
// user page number
function checkPageBounds(thisPageNum) {
if ( thisPageNum < 1 ) {
throw new Error( "Illegal page number, must be >=1 : " + thisPageNum );
}
if ( thisPageNum == 1 ) {
throw new Error( "Unable to handle first page, as it has no pagebreak" );
}
if ( thisPageNum > getMaxPageNumber() ) {
throw new Error( "Not enough pages in form to reference page number : " + thisPageNum );
}
}


/////////////////////////////////////////////////////////////////////////////
// Returns the item (page_break) at this page num
// pagenum is based on 1, as the users sees it
// We can't return for the number 1, as it has no pagebreak.
function getPageItem(thisPageNum) {
checkPageBounds(thisPageNum);
// Get the page it. First page is out of bounds, page 2 is array index 0
// So, substract 2
return getAllPageItems()[thisPageNum - 2];
}