pendingEvents.js

Summary

No overview generated for 'pendingEvents.js'


// This implements a "pending event" handling system. It wraps
// window.setTimeout with an interface that manages the cancellation
// or subsequent overriding transparently.

// While this copy of pendingEvents.js is distributed with xblinjs,
// this has a seperate license: pendingEvents.js is placed into the
// public domain by the author, Jeremy Bowers in 2005.

/**
 Maintains the list of pending event objects, used for correlating
 the numeric ids to the actual events.

 @private
*/
pendingEvents = {};

/**
 Used to give each event a unique id.

 @private
*/
uniqueEventIdentifier = 1;

/**
 A constant, defining the Javascript function type.
*/
FUNCTION_TYPE = typeof(function () {});

/**
 A constant, defining the Javascript number type.
*/
NUMBER_TYPE = typeof(5);

/**
 A constant, defining the Javascript string type.
*/
STRING_TYPE = typeof("");

/**
 Creates a new pending event.

 <p>A "pending event" is in essense a more intelligent version of the
 .setTimeout call (which is used to implement pending events). The
 best way to understand it is to start with a canonical example of
 one: Suppose you are writing a drop-down box attached to a text box, 
 and you want the dropdown box to fire after the user is idle for some
 amount of time. (Most browser address bars are like this.)</p>

 <p>The way to do that is to create a pendingEvent every time the
 user enters a character, with the same key used for each one; for
 concreteness let's call the key "dropDown". Each subsequent
 "dropDown" pendingEvent will <i>cancel</i> the previous one, so
 if each one is created with a half second delay, the net effect
 is that only the final half-second delay will fire the event handling
 code. As long as the user types more quickly than that, the handler
 never fires. This turns out to be very useful in UI code.</p>

 <p>Each pendingEvent can also have a cancel function which will
 be called every time it is cancelled, and pendingEvents can be
 manually cancelled by calling <tt>cancelEvent</tt>. In the dropdown
 example, that would most likely be done every time the user leaves 
 the text box, so the dropdown won't suddenly appear a half second 
 later when the user's attention is on another widget entirely.</p>

 @param key The unique key that identifies this pending event. Use
 this key to cancel the event with cancelEvent, or supercede the event
 with later calls to pendingEvent. (Only the last pendingEvent call
 for a given key will be used.)
 @param ms When to fire this pending event in milliseconds from the
 call time, as long as it is not cancelled or superceded. This is of
 course build on window.setTimeout, and subject to the same timing
 considerations: You are guaranteed to not have the event fire before
 the time has passed, but it can occur arbitrarily far into the
 future.
 @param exeStatement The statement to execute when the event
 fires. Can either be a string, or a closure function taking no
 arguments.
 @param cancelStatement The statement to execute when the event is
 cancelled, either by a call to cancelEvent or a superceding call to 
 pendingEvent. Can either be a string, or a closure function taking
 no arguments.
*/
function pendingEvent ( key, ms, exeStatement, cancelStatement )
{
  cancelPendingEvent ( key );

  if (typeof(ms) != NUMBER_TYPE) {
    throw ("Can't use '" + ms + "' as the millisecond parameter; must"
           + " use a number.");
  }
  if (typeof(exeStatement) != STRING_TYPE &&
      typeof(exeStatement) != FUNCTION_TYPE) {
    throw ("Can't use '" + exeStatement + "' as the statement to " +
           "execute; must be either string or function/closure.");
  }
  if (cancelStatement &&
      typeof(cancelStatement) != STRING_TYPE &&
      typeof(cancelStatement) != FUNCTION_TYPE) {
    throw ("Can't use '" + cancelStatement + "' as the statement to " +
           "cancel with; must be either string or function/closure.");
  }

  this.exeStatement = exeStatement;
  this.cancelStatement = cancelStatement;
  this.identifier = uniqueEventIdentifier++;

  pendingEvents[key] = this;

  window.setTimeout ( "executePendingEvent(\"" + key + "\", " + pendingEvents[key].identifier + ");", ms );
}

/**
 Manages actually executing the pending event.

 @private
*/
function executePendingEvent ( key, eventIdentifier )
{ 
  if ( pendingEvents[key] && 
       pendingEvents[key].identifier == eventIdentifier ) {
    statement = pendingEvents[key].exeStatement;
    if (typeof(statement) == typeof("")) {
      eval ( statement );
    } else {
      statement();
    }
    delete pendingEvents[key];
  }
}

/**
 Cancels the pending event indicated by "key". If the event had a
 cancelStatement, the cancel statement will be executed.

 @param key The key of the pending event to cancel.
*/
function cancelPendingEvent ( key )
{
  if ( pendingEvents[key] && pendingEvents[key].cancelStatement ) {
    statement = pendingEvents[key].cancelStatement;
    if (typeof(statement) == typeof("")) {
      eval ( statement );
    } else {
      statement();
    }
  }
  delete pendingEvents[key];
}




Documentation generated by JSDoc on Tue May 3 17:16:26 2005