Inactive Form Timeout

Someone asked me about setting up a way to inactivate a form if it is left open for too long.  There is no feature that can be turned on or configured, however you can build something like this into your form using javaScript.  I have created this example.  All the code included in the example has also been provided below so that you could re-build it or implement it in your own application.

Please note that there are several different ways that you can modify this sample to provide a different end user experience (i.e. redirect to other website, etc).  I have not covered all the scenarios and am simply providing the high-level theory behind implementing this kind of solution.  There are many other ways to do something like this but this one leverages the functions available and will work within a server.


Description

This form simulates a forced time-out.  If the form sits inactive for a specified duration then you can for the form to close or redirect to a different page.  This works by using the javaScript setTimeout and clearTimeout functions.

When the form loads we first walk all the input items in the form and attach a listener to the onItemChange event.  When a field changes the event listener will clear the current timeout and then start it over again.

If the duration is met then the form performs the action defined in the app.getSharedData().doTimeout function, which in this case is to switch to a custom page in the application that provides instructions to the user that their session timed out.

The duration for this sample is 5 seconds (5000 milliseconds), if you do nothing the page will flip.  If you change any field value the timer will be reset (note: you must tab out to indicate a field change).


Code

Several of the functions I have listed below are taken directly from the Using JavaScript in Your Applications section of this wiki.

onStart

/*
* This is the function where you place the logic that you want to perform on the item that you are currently looking at.
* The recursive function passes the handle to the current item, from which you can then access any of its properties
*/
app.getSharedData().processItem = function(item) {

  if(app.getSharedData().isInputField(item.getType())) {
     var hndl = item.connectEvent('onItemChange', function()
     {      
         //if any form item changes then we clear the timeout and start over
         clearTimeout(app.getSharedData().timeout);
         app.getSharedData().timeout = setTimeout(app.getSharedData().doTimeout, app.getSharedData().timeoutDuration);
       
     });   
     app.getSharedData().hndls.push({itm: item, hnd: hndl});
  }
}

app.getSharedData().isInputField = function(theItemType) {
  var r = false;

 if(theItemType === "text" || theItemType === "textArea" || theItemType === "date" ||
     theItemType === "checkGroup" || theItemType === "radioGroup" || theItemType === "number" ||
     theItemType === "currency" || theItemType === "comboBox" || theItemType === "horizontalSlider" ||
     theItemType === "choiceSlider" || theItemType === "time" || theItemType === "webLink" ||
     theItemType === "radioGroup" || theItemType === "horizontalSlider" || theItemType === "choiceSlider" ||
     theItemType === "surveyQuestion" || theItemType === "emailAddress" || theItemType === "password" || theItemType === "timeStamp") {
    r = true;
  }
  return r;
}

/*
* Returns true if the current item has children, otherwise false.
*/
app.getSharedData().hasItems = function(containerID) {
    var list = containerID.getChildren();
    if(list.getLength() > 0) {
     return true;
    } else {
        return false;
    }   
}

/*
* Recursive function used for counting form items.
* containerID: UI item (i.e. page or item)
* processItem: the function that contains the work we want to perform on the item we have accessed
*/
app.getSharedData().getItem = function(containerID, processItem) {
      var itemList;
      var pageList;
      var pageCount = 1;
      debugger;
      
      //check to see if the container is a form as it requires different processing
      if(containerID.getType() === "form") {
          pageList = containerID.getPageIds(); //list of the page IDs - not the actual objects!!
          pageCount = pageList.length;
      } else {
      itemList = containerID.getChildren();    
    }

    //need a loop to account for different pages
    for(var p=0; p<pageCount;p++) {
        if(containerID.getType() === "form") {
          itemList = containerID.getPage(get(pageList, p)).getChildren(); //get the page object from the form
        }
        
        //loop all the items
        for(var i=0; i<itemList.getLength(); i++)
        {
          var theItem = itemList.get(i);
          if(app.getSharedData().hasItems(theItem)) {
              //if container go into it...
                app.getSharedData().getItem(theItem, processItem);   
            } else {
                //other wise do something with the item
                if(dojo.isFunction(processItem)) { //make sure that the parameter passed is a function
                  processItem(theItem);        
                }
            }
        }
      }
}



Form onLoad

app.getSharedData().timeoutDuration = 5000;  //duration of timeout in milliseconds
app.getSharedData().timeout = null;      //holds the pointer to the current timeout process
app.getSharedData().hndls = new Array(); //holds the handles for all the onItemChange listeners

//defines what happens when the timeout is executed
//this action could be anything you want it to be
app.getSharedData().doTimeout = function() {
  form.selectPage("P_NewPage2");
}

//hook the timeout functions on all the form input items onItemChange event
//we use existing functions to perform this work found on the devworks wiki
app.getSharedData().getItem(form, app.getSharedData().processItem);

//start the timeout, if any form item changes then we clear the timeout and start over
app.getSharedData().timeout = setTimeout(app.getSharedData().doTimeout, app.getSharedData().timeoutDuration);



Form onDestruct

//disconnect all the handles.  We do this incase the form is ever reloaded in the same
//session, because it will attach new listeners which means there will be duplicates
for(var i=0; i< app.getSharedData().hndls.length;i++) {
  var itm = get(app.getSharedData().hndls, i).itm;
  var hndl = get(app.getSharedData().hndls, i).hndl;

  itm.disconnectEvent(hndl);
}