function $i()
{
    var elements = new Array();

    for ( var i = 0; i < arguments.length; i++ )
    {
        var element = arguments[i];

        if ( typeof element == 'string' )
        {
            element = document.getElementById( element );

            // If Prototype is supported, extend the element before returning it.
            if ( exists( element ) && exists( Element ) && exists( Element.extend ) )
            {
                element = Element.extend( element );
            }
        }

        if ( arguments.length == 1 )
        {
            return element;
        }

        elements.push( element );
    }

    return elements;
}

// If no event is specified, tries to get the most recent event.
function getEventTarget( e )
{
    var targ;

    if ( !e )
    {
        e = window.event;
    }

    if ( e.target )
    {
        targ = e.target;
    }
    else if ( e.srcElement )
    {
        targ = e.srcElement;
    }

    // defeat Safari bug
    if ( targ.nodeType == 3 )
    {
        targ = targ.parentNode;
    }

    return targ;
}

// This adds an event listener -- it is allowed to have multiple event listeners for the same type for the same object.
// Clears the set of events when the page is unloaded.
// Created by Dustin Diaz http://www.dustindiaz.com
// Tweaked by Salman Halim.
function addEvent( obj, type, fn )
{
    var attachedFunction = fn;

    if ( obj.addEventListener )
    {
        obj.addEventListener( type, fn, false );
    }
    else if ( obj.attachEvent )
    {
        obj[ "e" + type + fn ] = fn;
        obj[ type + fn ]       = function() { obj[ "e" + type + fn ]( window.event ); }
        attachedFunction       = obj[ type + fn ];

        obj.attachEvent( "on" + type, attachedFunction );
    }
    else if ( typeof obj[ 'on' + type ] == "function" )
    {
        var fOld = obj[ 'on' + type ];

        obj[ 'on' + type ] = function( e ){ fOld( e ); fn( e ); };
    }
    else
    {
        obj[ 'on' + type ] = fn;
    }

    EventCache.add( obj, type, attachedFunction );
}

// Works in tandem with addEvent above, removing precisely what it would've added.
function removeEvent( obj, type, fn, bubbleCapture )
{
    if ( obj.removeEventListener )
    {
        obj.removeEventListener( type, fn, bubbleCapture );
    }
    else if ( obj.detachEvent )
    {
        obj.detachEvent( 'on' + type, obj[ type + fn] );
    }

    obj[ 'on' + type ] = null;
}

var EventCache = function()
{
  var listEvents = [];
  return {
    listEvents : listEvents,
    add : function(node, sEventName, fHandler, bCapture){
      listEvents.push(arguments);
    },
    flush : function(){
      var i, item;
      for(i = listEvents.length - 1; i >= 0; i = i - 1){
        item = listEvents[i];
        if (item[0].removeEventListener){
          item[0].removeEventListener(item[1], item[2], item[3]);
        };
        if (item[1].substring(0, 2) != "on"){
          item[1] = "on" + item[1];
        };
        if (item[0].detachEvent){
          item[0].detachEvent(item[1], item[2]);
        };
        item[0][item[1]] = null;
      };
    }
  };
}();
addEvent(window,'unload',EventCache.flush);

// Hides the item; the item still takes up the space it did when visible.
function toggleVisibility( id )
{
    var style = $i( id ).style;

    style.visibility = style.visibility != 'hidden' ? 'hidden' : 'visible';
}

// Hides the item, reclaiming the space it took on-screen.
// Display also has 'block' and 'inline'; 'inline' is like the behaviour of <span> and 'block' is like <div>.
//
// Returns false, just in case it's called from an event handler.
function toggleDisplay( id )
{
    var style = $i( id ).style;

    style.display = style.display != 'none' ? 'none' : '';

    return false;
}

// We also have getElementById, getElementsByName() and getElementsByTagName(), but they're already built-in.
// Note that the second and third parameters are optional.
function getElementsByClass( searchClass, node, tag )
{
    var classElements = new Array();
    if ( node == null )
    {
        node = document;
    }

    if ( tag == null )
    {
        tag = '*';
    }

    var els     = node.getElementsByTagName( tag );
    var elsLen  = els.length;
    var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)");

    for ( var i = 0, j = 0; i < elsLen; i++ )
    {
        if ( pattern.test( els[i].className ) )
        {
            classElements[j] = els[i];
            j++;
        }
    }
    return classElements;
}

// To use, just call <array>.inArray( valueToCheck ).
Array.prototype.inArray = function ( value )
{
    var i;

    for ( i=0; i < this.length; i++ )
    {
        if ( this[i] === value )
        {
            return true;
        }
    }
    return false;
};

function getCookie( name )
{
    var start = document.cookie.indexOf( name + "=" );
    var len   = start + name.length + 1;

    if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) )
    {
        return null;
    }

    if ( start == -1 )
    {
        return null;
    }

    var end = document.cookie.indexOf( ";", len );

    if ( end == -1 )
    {
        end = document.cookie.length;
    }

    return unescape( document.cookie.substring( len, end ) );
}

function setCookie( name, value, expires, path, domain, secure )
{
    var today = new Date();

    today.setTime( today.getTime() );

    if ( expires )
    {
        expires = expires * 1000 * 60 * 60 * 24;
    }

    var expires_date = new Date( today.getTime() + (expires) );

    document.cookie = name + "=" + escape( value ) +
            ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + //expires.toGMTString()
            ( ( path ) ? ";path=" + path : "" ) +
            ( ( domain ) ? ";domain=" + domain : "" ) +
            ( ( secure ) ? ";secure" : "" );
}

function deleteCookie( name, path, domain )
{
    if ( getCookie( name ) )
    {
        document.cookie = name + "=" +
                ( ( path ) ? ";path=" + path : "") +
                ( ( domain ) ? ";domain=" + domain : "" ) +
                ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
    }
}

// Moves the item with ID 'what' to just after the item with ID 'where'.
// The screen redraw isn't perfect.
function movePar( what, where )
{
    if ( !document.createElement )
    {
        alert( 'This script won\'t work in your browser.' );
        return;
    }
    $i( where ).appendChild( $i( what ) );
}

// Given an element (such as a form element) and a property name, will return the value if it exists and null if not.
function getSafePropertyValue( element, property )
{
    return exists( element[ property ] ) ? element[ property ] : null;
}

// This is better than element.style.attribute because it returns a value even if none has been explicitly specified in
// the style.
function getStyle( oElm, strCssRule )
{
    var strValue = "";
    if ( document.defaultView && document.defaultView.getComputedStyle )
    {
        strValue = document.defaultView.getComputedStyle( oElm, "" ).getPropertyValue( strCssRule );
    }
    else if ( oElm.currentStyle )
    {
        strCssRule = strCssRule.replace(/\-(\w)/g, function ( strMatch, p1 )
                                        {
                                        return p1.toUpperCase();
                                        });
        strValue = oElm.currentStyle[ strCssRule ];
    }
    return strValue;
}

function getPosition( theElement )
{
    var positionX = 0;
    var positionY = 0;

    while ( theElement != null )
    {
        positionX += theElement.offsetLeft;
        positionY += theElement.offsetTop;
        theElement = theElement.offsetParent;
    }

    return [ positionX, positionY ];
}

// insertBefore exists; this basically says:  if the current item has another item after it, perform an insertBefore for
// it.  If it doesn't (the second parameter becomes null), the DOM will automatically add it to the bottom of all the
// children, making it the new last element.
function insertAfter( targetElement, newElement )
{
    var parent = targetElement.parentNode;

    if ( parent.lastChild == targetElement )
    {
        parent.appendChild( newElement );
    }
    else
    {
        parent.insertBefore( newElement, targetElement.nextSibling );
    }
}

String.prototype.trim = function( charlist )
{
    var chars = charlist || "\\s";

    return this.replace( new RegExp( "^[" + chars + "]+|[" + chars + "]+$", "g" ), "" );
}

String.prototype.ltrim = function( charlist )
{
    var chars = charlist || "\\s";

    return this.replace( new RegExp( "^[" + chars + "]+", "g" ), "" );
}

String.prototype.rtrim = function( charlist )
{
    var chars = charlist || "\\s";

    return this.replace( new RegExp( "[" + chars + "]+$", "g" ), "" );
}

function setOpacity( element, alpha )
{
    var style = element.style;

    if ( style.MozOpacity != undefined )
    {
        //Moz and older
        style.MozOpacity = alpha;
    }
    else if ( style.filter != undefined )
    {
        //IE
        style.filter                  = "alpha(opacity=0)";
        element.filters.alpha.opacity = ( alpha * 100 );
    }
    else if ( style.opacity != undefined )
    {
        //Opera
        style.opacity = alpha;
    }
}

function addClassName (elem, className) {
    removeClassName (elem, className);
    elem.className = (elem.className + " " + className).trim();
}

function removeClassName ( elem, className )
{
    elem.className = elem.className.replace( className, "" ).trim();
}

// Purpose:  Determine whether or not an optional function parameter has been passed in.
// TODO:  Should this not check for null because null is not the result of a non-existent value?
function exists( variable )
{
    return typeof( variable ) != 'undefined' && variable != null;
}

// Checks that either the parameter value doesn't exist or, if it does exist, it's a string of zero length.
function isEmpty( str )
{
    return !exists( str ) || str.length == 0;
}

function addLoadEvent( func )
{
    var oldonload = window.onload;

    if ( typeof window.onload != 'function' )
    {
        window.onload = func;
    }
    else
    {
        window.onload = function()
        {
            oldonload();
            func();
        }
    }
}

// The second parameter is an optional comma or space separated set of properties to actually return -- only those that
// are not null are returned.
function getProperties( obj, title, properties )
{
    var result = '';

    if ( exists( properties ) )
    {
        var props = properties.split( /[ ,]+/ );

        for ( var i in props )
        {
            var property = props[ i ];

            if ( obj[ property ] != null )
            {
                result = result + "\n" + property + "=" + obj[ property ];
            }
        }
    }
    else
    {
        for ( property in obj )
        {
            if ( obj[ property ] != null )
            {
                result = result + "\n" + property + "=" + obj[ property ];
            }
        }
    }

    if ( exists( title ) )
    {
        result = "Properties for " + title + "\n" + result;
    }

    return result;
}

function showProperties( obj, title, properties )
{
    myAlert( getProperties( obj, title, properties ) );
}

/*
Sample debug style:
.debug
{
    background-color: #F0F0F0;
    color:            #333;
    font-size:        1.5em;
}
 */
function myAlert( val )
{
    var contentElement = document.createElement( 'pre' );

    // FireFox doesn't have innerText.
    if ( exists( contentElement.innerText ) )
    {
        contentElement.innerText = val;
    }
    else
    {
        contentElement.innerHTML = val;
    }

    addClassName( contentElement, 'debug' );

    document.body.appendChild( document.createElement( 'hr' ) );
    document.body.appendChild( contentElement );
}

// Creates a new object that contains all the properties of the source object.
function copyProperties( obj )
{
    var result = new Object();

    for ( var i in obj )
    {
        result[ i ] = obj[ i ];
    }

    return result;
}

// Returns the X and Y coordinates of the mouse cursor in an event; useful for placing a tooltip relative to the mouse
// cursor, for example.
function mouseX(evt) {
if (evt.pageX) return evt.pageX;
else if (evt.clientX)
   return evt.clientX + (document.documentElement.scrollLeft ?
   document.documentElement.scrollLeft :
   document.body.scrollLeft);
else return null;
}
function mouseY(evt) {
if (evt.pageY) return evt.pageY;
else if (evt.clientY)
   return evt.clientY + (document.documentElement.scrollTop ?
   document.documentElement.scrollTop :
   document.body.scrollTop);
else return null;
}

// FireFox doesn't have an innerText attribute so this one defaults to the innerHTML as needed.
function getInnerText( element )
{
    return exists( element.innerText ) ? element.innerText : element.innerHTML;
}

// Disables all form fields, except those whose names match the optional specified regular expression.  Returns a space-separated list of fields
// that were disabled (this can be passed back to reEnableForm).
function disableForm( form, exceptions )
{
    var disabledFields = "";

    for ( var i in form.elements )
    {
        if ( exists( form.elements[ i ] ) && exists( form.elements[ i ].disabled ) )
        {
            if ( exists( exceptions ) && exceptions.test( getSafePropertyValue( form.elements[ i ], "name" ) ) )
            {
                continue;
            }

            var tagName = getSafePropertyValue( form.elements[ i ], "tagName" );

            if ( ( /TEXTAREA|INPUT|SELECT/.test( tagName ) ) && !form.elements[ i ].disabled )
            {
                form.elements[ i ].disabled = true;

                if ( disabledFields.length > 0 )
                {
                    disabledFields = disabledFields + " ";
                }

                disabledFields = disabledFields + i;
            }
        }
    }

    return disabledFields;
}

// Given a form and a list of space-separated form fields, makes sure the specified fields are enabled.  Useful as the opposite of the call to
// disableForm.
function reEnableForm( form, disabledFields )
{
    var fields = disabledFields.split( / / );

    for ( var i = 0; i < fields.length; i++ )
    {
        form.elements[ fields[ i ] ].disabled = false;
    }
}

// Used to ensure a disabled link doesn't do anything.
function disabledOnclick()
{
    return false;
}

// Enables or disables all links on a page.
function enableDisableLinks( enabled )
{
    for ( var i = 0; i < document.links.length; i++ )
    {
        var link = document.links[ i ];

        if ( enabled )
        {
            removeClassName( link, "disabledLink" );

            link.onclick = exists( link.oldOnclick ) ? link.oldOnclick : null;
        }
        else
        {
            addClassName( link, "disabledLink" );

            if ( exists( link.onclick ) )
            {
                link.oldOnclick = link.onclick;
            }

            link.onclick = disabledOnclick;
        }
    }
}

// Adds a cross-browser CSS rule.
//
// Example call:
//
// addCssRule( 'div.footer', 'background-color: #FFF; color: #000;' );
function addCssRule( selector, styles )
{
    var styleSheet = document.styleSheets[0];

    if ( styleSheet.insertRule )
    {
        styleSheet.insertRule( selector + ' { ' + styles + ' }', styleSheet.cssRules.length );
    }
    else if ( styleSheet.addRule )
    {
        styleSheet.addRule( selector, styles );
    }
}

// Removes the CSS rule at the specified index.  Is cross-browser.
function removeCssRule( index )
{
    if ( oSheet.deleteRule )
    {
        oSheet.deleteRule( index );
    }
    else if ( oSheet.removeRule )
    {
        oSheet.removeRule( index );
    }
}

function isEmailValid( email )
{
    return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test( email );
}

// Gets the next HTML tag -- basically a nextSibling call that skips over inter-tag whitespace.
//
// If a tagName is specified, will skip over siblings to return the first sibling that has the specified tag type.  For
// example, to find the next SPAN from the current element in an event handler, call with nextTag( event, 'SPAN' ).
function nextTag( element, tagName )
{
    var result = element.nextSibling;

    if ( exists( tagName ) )
    {
        while ( result != null && ( result.nodeType != 1 || result.nodeName != tagName ) )
        {
            result = result.nextSibling;
        }
    }
    else
    {
        while ( result != null && result.nodeType != 1 )
        {
            result = result.nextSibling;
        }
    }

    return result;
}

// Height and width of current browser window.
function getFrameSize()
{
    var result = new Object();

    if ( self.innerWidth )
    {
        result.width  = self.innerWidth;
        result.height = self.innerHeight;
    }
    else if ( document.documentElement && document.documentElement.clientWidth )
    {
        result.width  = document.documentElement.clientWidth;
        result.height = document.documentElement.clientHeight;
    }
    else if ( document.body )
    {
        result.width  = document.body.clientWidth;
        result.height = document.body.clientHeight;
    }

    return result;
}

function getScrollPos()
{
    var result = new Object();

    /*
    if ( self.pageXOffset )
    {
        result.scrollX = self.pageXOffset;
        result.scrollY = self.pageYOffset;
    }
    else if ( document.documentElement && document.documentElement.scrollLeft )
    {
        result.scrollX = document.documentElement.scrollLeft;
        result.scrollY = document.documentElement.scrollTop;
    }
    else if ( document.body )
    {
        result.scrollX = document.body.scrollLeft;
        result.scrollY = document.body.scrollTop;
    }
     */
    var iebody = ( document.compatMode && document.compatMode != "BackCompat" ) ? document.documentElement : document.body;

    result.scrollX = document.all? iebody.scrollLeft : pageXOffset;
    result.scrollY = document.all? iebody.scrollTop : pageYOffset;

    return result;
}

// Returns a pair of coordinates suitable for setting the desired component's (tooltip or dialog box, for example) left
// and top values to position the component accordingly.
//
// The position is one of:
//
// "mouse":  Slightly below and to the right of the mouse cursor -- this is the default.
//
// "right":  Immediately to the right of the item for which the tooltip is to be displayed, the top edges of the two
// aligned.
//
// "below":  Immediately below the item for which the tooltip is to be displayed, the left edges of the two aligned.
//
// "overlay":  Placed right on top of the component.
//
// "center":  Centered on top of the component.  If the component is too narrow (narrower than the dialog box), then
// vertically centered to the right of the component.  If the component is too short (not tall enough), then
// horizontally centered at the bottom of the component.  If the component is too narrow AND too short, then below the
// component (as if "below" had been chosen instead).
//
// "pageCenter":  Centered on the current screen.
//
// If a third parameter is provided, it is the object to be placed; depending on the type of position ("center", for
// example), its width and height may have to be considered.
function getComponentPosition( event, position, object )
{
    var coordinates = new Object();
    var target      = getEventTarget( event );
    var targetPos   = getPosition( target );

    if ( !exists( position ) || position == "mouse" )
    {
        coordinates.left = mouseX( event ) + 10 + 'px';
        coordinates.top  = mouseY( event ) + 10 + 'px';
    }
    else if ( position == "right" )
    {
        coordinates.left = targetPos[ 0 ] + target.offsetWidth + 2 + 'px';
        coordinates.top  = targetPos[ 1 ] + 'px';
    }
    else if ( position == "below" )
    {
        coordinates.left = targetPos[ 0 ] + 'px';
        coordinates.top  = targetPos[ 1 ] + target.offsetHeight + 2 + 'px';
    }
    else if ( position == "overlay" )
    {
        coordinates.left = targetPos[ 0 ] + 'px';
        coordinates.top  = targetPos[ 1 ] + 'px';
    }
    else if ( position == "center" )
    {
        coordinates.left = targetPos[ 0 ] + ( target.offsetWidth - object.offsetWidth ) / 2;
        coordinates.top  = targetPos[ 1 ] + ( target.offsetHeight - object.offsetHeight ) / 2;

        if ( coordinates.left < targetPos[ 0 ] && coordinates.top < targetPos[ 1 ] )
        {
            coordinates = getComponentPosition( event, "below" );
        }
        else if ( coordinates.left < targetPos[ 0 ] )
        {
            coordinates.left  = getComponentPosition( event, "right" ).left;
            coordinates.top  += 'px';
        }
        else if ( coordinates.top < targetPos[ 1 ] )
        {
            coordinates.left += 'px';
            coordinates.top   = getComponentPosition( event, "below" ).top;
        }
        else
        {
            coordinates.left += 'px';
            coordinates.top  += 'px';
        }
    }
    else if ( position == "pageCenter" )
    {
        var pageSize   = getFrameSize();
        var pageScroll = getScrollPos();

        coordinates.left = pageScroll.scrollX + ( pageSize.width - object.offsetWidth ) / 2 + 'px';
        coordinates.top  = pageScroll.scrollY + ( pageSize.height - object.offsetHeight ) / 2 + 'px';
    }

    return coordinates;
}

String.prototype.substitute = function( was, becomes )
{
      return this.split( was ).join( becomes );
};

// If the name passed in is a complete URL to a script, this strips out the last part of the path and returns it.
//
// Otherwise, it looks through all the script tags already placed into the page to find the one matching the name passed
// in and returns the path portion of that instead.
//
// If it can't find the path at all, returns the empty string.
function getPathOfScript( name )
{
    var path  = "";
    var regex = new RegExp( name.substitute( /\./, "\\." ) + "(\\?.*)?$" );

    // Ignore scripts that start with things like http: and file: or even c:
    if ( /(^[a-zA-Z]+:)|(^[\\\/])/.test( name ) )
    {
        path = name.replace( /[^/]+(\?.*)?$/, '' );
    }
    else
    {
        var scripts = document.getElementsByTagName( "script" );

        for ( var i = 0; i < scripts.length; i++ )
        {
            if ( regex.test( scripts[ i ].src ) )
            {
                path = scripts[ i ].src.replace( regex, '' );

                break;
            }
        }
    }

    return path;
}

// Once this script has been sourced, other scripts with dependencies can simply source those dependencies by calling
// this function.
//
// If an optional basePath is sent in and the name is a relative path, the basePath is prepended.  Otherwise, the path
// of the current script (common) is used and prepended instead.
//
// We do this via direct HTML insertion because apparently not all browsers are crazy about direct DOM manipulation.
function includeScript( name, basePath )
{
    var path = name;

    // Do not process scripts that start with things like http: and file: or even c:
    if ( !/(^[a-zA-Z]+:)|(^[\\\/])/.test( name ) )
    {
        path = exists( basePath ) ? basePath : getPathOfScript( "common.js" );
        path += name;
    }

    document.write( '<script type="text/javascript" src="' + path + '"></script>' );
}

// Dropdowns can be turned on or off -- IE shows them above all other layers for some reason so while displaying a
// dialog box it's better to turn them off.
function toggleDropDowns( visible )
{
    var selectElems = document.getElementsByTagName( 'select' );
    for( var i = 0; i < selectElems.length; i++ )
    {
        selectElems[ i ].style.visibility = visible;
    }
}

/* parseUri JS v0.1, by Steven Levithan (http://badassery.blogspot.com)
Splits any well-formed URI into the following parts (all are optional):
----------------------
- source (since the exec() method returns backreference 0 [i.e., the entire match] as key 0, we might as well use it)
- protocol (scheme)
- authority (includes both the domain and port)
    - domain (part of the authority; can be an IP address)
    - port (part of the authority)
- path (includes both the directory path and filename)
    - directoryPath (part of the path; supports directories with periods, and without a trailing backslash)
    - fileName (part of the path)
- query (does not include the leading question mark)
- anchor (fragment)
*/
function parseUri(sourceUri){
    var uriPartNames = ["source","protocol","authority","domain","port","path","directoryPath","fileName","query","anchor"];
    var uriParts = new RegExp("^(?:([^:/?#.]+):)?(?://)?(([^:/?#]*)(?::(\\d*))?)?((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[\\?#]|$)))*/?)?([^?#/]*))?(?:\\?([^#]*))?(?:#(.*))?").exec(sourceUri);
    var uri = {};

    for(var i = 0; i < 10; i++){
        uri[uriPartNames[i]] = (uriParts[i] ? uriParts[i] : "");
    }

    // Always end directoryPath with a trailing backslash if a path was present in the source URI
    // Note that a trailing backslash is NOT automatically inserted within or appended to the "path" key
    if (uri.directoryPath.length > 0){
        uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/");
    }

    return uri;
}

/*
createElement function found at http://simon.incutio.com/archive/2003/06/15/javascriptWithXML

Modified by Salman Halim on March 30, 2007 (01:36:09) to take extra parameters where each parameter pair is an attribute name and value.  Also, other elements
can be passed in (though not in between a name and value pair) and get added as child elements.

Special attribute 'text' adds the value of the "attribute" as a child text node.

For example:  createElement( 'div', createElement( 'input', 'type', 'checkbox' ), 'class', "test" )

Gives:  <div class="test"><input type="checkbox" /></div>
*/
function createElement(element)
{
    var result = false;

    if (typeof document.createElementNS != 'undefined')
    {
        result = document.createElementNS('http://www.w3.org/1999/xhtml', element);
    }

    if (typeof document.createElement != 'undefined')
    {
        result = document.createElement(element);
    }

    if ( result )
    {
        for ( var i = 1; i < arguments.length; i++ )
        {
            // We were passed in a child element
            if ( typeof arguments[ i ] == 'string' )
            {
                if ( arguments[ i ] == 'text' )
                {
                    setText( result, arguments[ ++i ] );
                }
                else
                {
                    // Get this parameter and the next one as the value.
                    result.setAttribute( arguments[ i ], arguments[ ++i ] );
                }
            }
            else
            {
                // Set it as a child.
                result.appendChild( arguments[ i ] );
            }
        }
    }

    return result;
}

// Convenient method to add text to an element.
function setText( element, text )
{
    element.appendChild( document.createTextNode( text ) );
}

// Returns the value of the 'for' attribute for a label.
function getLabelId( el )
{
    var forId = el.getAttribute( 'for' );

    if ( forId == null )
    {
        forId = el.htmlFor;
    }

    return forId;
}

/**
 * Determines if a form is dirty by comparing the current value of each element
 * with its default value.
 *
 * @param {Form} form the form to be checked.
 * @return {Boolean} true if the form is dirty, false otherwise.
 */
function isFormDirty( form )
{
    for ( var i = 0; i < form.elements.length; i++ )
    {
        var element = form.elements[ i ];
        var type = element.type;

        if ( type == "checkbox" || type == "radio" )
        {
            if ( element.checked != element.defaultChecked )
            {
                return true;
            }
        }
        else if ( type == "hidden" || type == "password" || type == "text" || type == "textarea" )
        {
            if ( element.value != element.defaultValue )
            {
                return true;
            }
        }
        else if ( type == "select-one" || type == "select-multiple" )
        {
            for ( var j = 0; j < element.options.length; j++ )
            {
                if ( element.options[ j ].selected != element.options[ j ].defaultSelected )
                {
                    return true;
                }
            }
        }
    }
    return false;
}

function openWindow( url, opt )
{
    if ( opt == 0 )
    {
        // current window
        window.location = url;
    }
    else if ( opt == 1 )
    {
        // new window
        window.open( url );
    }
    else if ( opt == 2 )
    {
        // background window
        window.open( url );
        self.focus();
    }
}

/*
Will either check or uncheck all checkboxes that have IDs of the form PREFIXn where PREFIX is passed in as a parameter and n is a number, starting with 1.

For example, passing in 'status_' will check all checkboxes with IDs of 'status_1', 'status_2', etc.

No gaps are allowed:  'status_3' must be followed by 'status_4', not 'status_5' or the system will return, thinking it's done.

Returns false to allow being called from an onClick handler.
 */
function selectAllCheckboxes( checkboxIdPrefix, checked )
{
    var i        = 1;
    var checkbox = $i( checkboxIdPrefix + i );

    while ( exists( checkbox ) )
    {
        checkbox.checked = checked;

        checkbox = $i( checkboxIdPrefix + ++i );
    }

    return false;
}
