/**
 * The RRS object is the single global object used by all RRS javascript files.  It contains a utility function for setting up namespaces.
 * @module RRS
*/
if ( typeof RRS == "undefined" )
{
    RRS = {};
};

/**
 * <p>Returns the namespace specified and creates it if it doesn't exist.</p>
 * 
 * <p><code>RRS.namespace("package.property");</code></p>
 * 
 * <p><code>RRS.namespace("RRS.packages.property");</code></p>
 * 
 * <p>Either of the above would create RRS.package, then RRS.package.property</p>
 * @class namespace
 * @namespace RRS
 * @param {String} ns The name of the namespace object
 * @return {Object} A reference to the namespace object
*/
RRS.namespace = function(ns)
{
    if ( !ns || !ns.length )
    {
        return null;
    }

    var levels = ns.split(".");
    var nsobj = RRS;
    var i;

    // RRS is implied, so it is ignored if it is included
    for ( i = ( levels[0] === "RRS" ) ? 1 : 0; i < levels.length; i++ )
    {
        nsobj[levels[i]] = nsobj[levels[i]] || {};
        nsobj = nsobj[levels[i]];
    }
    
    return nsobj;
};

RRS.namespace("util");
RRS.namespace("app");
RRS.namespace("eo");


/**
 * Helpful methods added to built-in objects
 * @module category
 * @namespace RRS
*/

/** 
 * Methods added to the built-in String class
 * @class String
 * @namespace RRS.category
*/

/**
 * <p>Based upon a function in the <a href="http://dojotoolkit.org">Dojo toolkit</a></p>
 * 
 * <p>Trim whitespace.  If loc > 0, only trim from start.  If loc < 0, only trim from the end.  Otherwise, trim both ends.</p>
 * @method trim
 * @extends String
 * @param loc {int} (optional) Indicates where to trim
 * @return {String} The string with the whitespace trimmed
*/
// Check if it already exists -- don't override any function added in future
// javascript implementations
if ( !String.prototype.trim ) String.prototype.trim = function(loc)
{
    var re;
        
    if ( !this.length )
        return;
        
    if ( loc > 0 )
        re = (/^\s+/);
        
    else if ( loc < 0 )
        re = (/\s+$/);
        
    else
        re = (/^\s+|\s+$/g);
        
    return this.replace(re,"");
};

/**
 * <p>Based upon a function in the <a href="http://dojotoolkit.org">Dojo toolkit</a></p>
 * 
 * <p>Trim whitespace at the beginning</p>
 * @method trimStart
 * @extends String
 * @return {String} The string with the whitespace stripped from the beginning
*/
if ( !String.prototype.trimStart ) String.prototype.trimStart = function()
{
    return this.trim(1);
};

/**
 * <p>Based upon a function in the <a href="http://dojotoolkit.org">Dojo toolkit</a></p>
 * 
 * <p>Trim whitespace at the end</p>
 * @method trimEnd
 * @extends String
 * @return {String} The string with the whitespace stripped from the end
*/
if ( !String.prototype.trimEnd ) String.prototype.trimEnd = function()
{
    return this.trim(-1);
};

/**
 * <p>Based upon a function in the <a href="http://dojotoolkit.org">Dojo toolkit</a></p>
 *
 * <p>Looks to see if the string is made up of only whitespace characters.</p>
 * @method isBlank
 * @extends String
 * @return {Boolean} true if the string is all whitespace, false if not
*/
if ( !String.prototype.isBlank ) String.prototype.isBlank = function()
{
    if ( !this.length )
        return true;
    else
        return ( this.trim().length == 0 );
};

/**
 * Checks the string for only numeric characters.  It will accept thousands separator (','), whitespace, dollar sign ('$'), negative sign (-), and decimal ('.').  You can pass in an array of characters to accept if you want to override this behavior.  For instance, if you want only numbers (no whitespace, commas, etc.), then pass in an empty array.
 * @method isNumber
 * @extends String
 * @param acceptChars {Array} The characters to accept in addition to numerical ones
 * @return {Boolean} Returns true if the string contains only numerical characters and the accepted characters; false if any other character is present or if the string is empty
*/
if ( !String.prototype.isNumber ) String.prototype.isNumber = function(acceptChars)
{
    var i,len,testChar;

    if ( !this.length )
        return false;
        
    if ( !acceptChars )
        acceptChars = new Array("\t","\n","\r","\f"," ","$",",",".","-");

    for ( i = 0, len = this.length; i < len; i++ )
    {
        testChar = this.charAt(i);
        
        if ( isNaN(parseInt(testChar,10)) && acceptChars.indexOf(testChar) == -1 )
            return false;
    }

    return true;
};

/**
 * <p>This will parse through the string and return a valid integer without whitespace, thousands separator, currency symbol, etc.  If the  value is not a whole number, it will round the final result.</p>
 *
 * <p>You can pass in an array of characters to ignore.  These will not be included in the result, but they will not stop processing, either.  The default set is the comma and the dollar sign.  If you don't want any characters to be ignored, then pass in an empty array.</p>
 * @method intValue
 * @extends String
 * @param ignoreChars {Array} (optional) An array of characters that can safely be ignored; they will not be included in the final result, but they will be stripped out and will not stop processing. 
 * @return {int} The integer value of the string if parsed successfully, NaN if not
*/
if ( !String.prototype.intValue ) String.prototype.intValue = function(ignoreChars)
{
    var i,len,resultStr,resultInt,testString,testChar,rounder;
    
    // Strip off whitespace
    testString = this.trim();
    
    // The characters that we'll ignore; other characters (except for
    // numerical ones) will be deal-breakers.
    if ( !ignoreChars )
        ignoreChars = new Array(",", "$");
        
    resultStr = "";
    for ( i = 0, len = testString.length; i < len; i++ )
    {
        testChar = testString.charAt(i);
        
        // Is it a number?
        if ( !isNaN(parseInt(testChar,10)) )
            resultStr += testChar;
            
        // Not a number.  Is it ignorable?
        else if ( ignoreChars.indexOf(testChar) != -1 )
            // Skip it and go on to the next character
            continue;
            
        // Was not a number and we can't ignore it.  
        // Is it a decimal?
        else if ( testChar == "." )
        {
            // Yup.  Get the next char and see if it is an integer
            // and try to round our result
            rounder = parseInt(testString.charAt(i+1),10);
            if ( rounder != NaN )
            {
                // We have a number.  Decide if we're rounding
                // up or not.
                if ( rounder >= 5 )
                {
                    // We're rounding up, so we need to get the 
                    // last number and add one to it.
                    testChar = resultStr.charAt(resultStr.length - 1);
                    testChar = parseInt(testChar,10) + 1;
                    
                    // Now we need to remove the last character and
                    // replace it with this one.
                    resultStr = resultStr.substring(0,(resultStr.length - 1));
                    resultStr += testChar;
                }
            }
            
            // Regardless of what happened above, we're at the end
            // of our processing.
            break;
        }
        
        // Is it a negative sign?  this is only okay
        // if it is the first character
        else if ( testChar == "-" && i == 0 )
        {
            resultStr += testChar;
            continue;
        }
        
        else 
        {
            // Was not a number, an ignorable character, or a decimal,
            // so we quit here.
            resultStr = "";
            break;
        }
    }
    
    if ( resultStr != "" )
        return resultStr;
    else
        return NaN;
};

/**
 * <p>This will parse through the string and return a valid float without whitespace, thousands separator, currency symbol, etc.</p>
 *
 * <p>You can pass in an array of characters to ignore.  These will not be included in the result, but they will not stop processing, either.  The default set is the comma and the dollar sign.  If you don't want any characters to be ignored, then pass in an empty array.</p>
 * @method floatValue
 * @extends String
 * @param ignoreChars {Array} (optional) An array of characters that can safely be ignored; they will not be included in the final result, but they will be stripped out and will not stop processing. 
 * @return {float} The float value of the string if parsed successfully, NaN if not
*/
if ( !String.prototype.floatValue ) String.prototype.floatValue = function(ignoreChars)
{
    var i,len,resultStr,resultInt,testString,testChar;
    
    // Strip off whitespace
    testString = this.trim();
    
    // The characters that we'll ignore; other characters (except for
    // numerical ones) will be deal-breakers.
    if ( !ignoreChars )
        ignoreChars = new Array(",", "$");
        
    resultStr = "";
    for ( i = 0, len = testString.length; i < len; i++ )
    {
        testChar = testString.charAt(i);
        
        // Is it a number or a decimal point?
        if ( !isNaN(parseInt(testChar,10)) || testChar == '.' )
            resultStr += testChar;
            
        // Not a number.  Is it ignorable?
        else if ( ignoreChars.indexOf(testChar) != -1 )
            // Skip it and go on to the next character
            continue;
            
        // Is it a negative sign?  This is only okay if it is the
        // first character
        else if ( testChar == "-" && i == 0 )
        {
            resultStr += testChar;
            continue;
        }
            
        else 
        {
            // Was not a number, an ignorable character, or a decimal,
            // so we quit here.
            resultStr = "";
            break;
        }
    }
    
    if ( resultStr != "" )
        return resultStr;
    else
        return NaN;
};


/** 
 * Methods added to the built-in Array class
 * @class Array
 * @namespace RRS.category
*/

/**
 * Returns the index of the object in the array; this actually is in Javascript 1.6, but that isn't supported yet by IE.  We check for the function first, though, so it is no big deal.  When 1.6 is supported we can get rid of this function.
 * @method indexOf
 * @extends Array
 * @param searchEl {Object} The element to find in the array
 * @param fromIndex {int} (optional) The index to start the search; defaults to 0
 * @return {int} The index of the object in the array or -1 if it isn't there
*/
if ( !Array.prototype.indexOf ) Array.prototype.indexOf = function(searchEl,fromIndex)
{
    var i,len;
    
    // Make sure that if there was a starting point passed
    // in that it is within the array's bounds.  If there was
    // none or it is negative, we start at the beginning of the array
    if ( !fromIndex || fromIndex < 0 )
        fromIndex = 0;
    
    // If fromIndex is larger than the biggest possible index,
    // we don't bother and return -1
    else if ( fromIndex > ( this.length - 1 ) )
        return -1;
    
    for ( i = fromIndex, len = this.length; i < len; i++ )
    {
        if ( this[i] == searchEl )
            return i;
    }
    
    // We get to this point if the element wasn't found,
    // so return -1
    return -1;
};



/**
 * Generally helpful classes and methods
 * @module util
 * @namespace RRS
*/

/**
 * This allows a consistent way of creating new classes.  Currently this just allows for an initialize function that you can then edit in the prototype for your new class.  Then you can set up the initialization for each instance of your new class.
 * @class Class
 * @namespace RRS.util
*/
RRS.util.Class = 
{
    /**
    * This simply returns an anonymous function which calls 'initialize' and passes in any arguments that were passed in. 
     * @method create
    */
    create: function() 
    {
        return function() { this.initialize.apply(this, arguments); };
    }
};

/**
 * Provides helper functions for DOM elements
 * @class Dom
 * @namespace RRS.util
*/
RRS.util.Dom = function() 
{
    var property_cache = {}; // to cache case conversion
    var style_cache = {}; // to cache original styles of the elements
    var _rowDisplay = "";
    
    var toCamel = function(property)
    {               
        // An internal function that replaces each occurance of 
        // a hyphen to the uppercase letter of the next word.
        // This allows us to call it in a loop to get all occurances
        // of the hyphen.
        var convert = function(prop)
        {
            var test = /(-[a-z])/i.exec(prop);
            return prop.replace(RegExp.$1, RegExp.$1.substr(1).toUpperCase());
        };
        
        while ( property.indexOf('-') > -1 )
        {
            property = convert(property);
        }
        
        return property;
    };

    var toHyphen = function(property)
    {
        var converted, i, len;

        // If there is one hyphen, assume it has already been
        // converted to the proper format and return as-is
        if ( property.indexOf('-') > -1 )
            return property;

        converted = '';
        for ( i = 0, len = property.length; i < len; i++ )
        {
            if ( property.charAt(i) == property.charAt(i).toUpperCase())
                converted = converted + '-' + property.charAt(i).toLowerCase();
            else
                converted = converted + property.charAt(i);
        }

        return converted;
    };

    // Cache all of the converted property names to improve performance
    var cacheConvertedProperties = function(property)
    { 
        property_cache[property] = 
        {
            camel: toCamel(property),
            hyphen: toHyphen(property)
        };
    };
    
    var cacheOriginalStyle = function(el,property)
    {
        var styleArray;
        
        styleArray = style_cache[el];
        if ( !styleArray )
            styleArray = [];
            
        styleArray[property] = RRS.util.Dom.getStyle(el,property);
        
        style_cache[el] = styleArray;
    };
    
    var cachedStyle = function(el,property)
    {
        var styleArray;
        
        styleArray = style_cache[el];
        if ( !styleArray )
            return null;
            
        // We have an array, but is there a value
        // for the specific property we're looking for?
        if ( styleArray[property] )
            return styleArray[property];
        else
            return null;
    };
    
        
    return  {
    
        /**
         * This function was lifted from the <a href="http://prototype.conio.net"> Prototype library</a>.  If you pass in one element, you will get a reference to just that element.  If you pass in more than one ID, you will get an array of element references.  This will allow you to code without all of those pesky document.getElementById calls.
         * @method get
        * @param el {String/HTMLElement/Array} Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements
        * @return A DOM reference to an HTML element or an array of HTMLElements
        */
        get: function(el)
        {
            var elements,i;
        
            if ( !el )
                return null;
            
            if ( typeof el != 'string' && !(el instanceof Array) )
                // Assume it is an HTMLElement and return as-is
                return el;
        
            else if ( typeof el == 'string' )
            {
                // Get a reference to it and return that
                return document.getElementById(el);
            }

            else
            {
                // Assume we have an Array
                elements = [];
                for ( i = 0, len = el.length; i < len; i++ )
                {
                    elements[elements.length] = RRS.util.Dom.get(el[i]);
                }
            
                return elements;
            }
        },
    
        /**
         * This is a convenience method mostly for other functions that need an array of objects to work with.  It guarantees to return an Array, even if it is empty or only has one member.  Also, the objects returned are HTMLElements, even if the array passed in had IDs in it.
         * @method getArray
         * @param el {String/HTMLElement/Array} Accepts a string to use as the ID of a element, a reference to the HTMLElement itself, or an Array of IDs/HTMLElements
         * @return {Array} An array of HTMLElements
        */
        getArray: function(el)
        {                       
            // There is only one object, so we'll use the get function and
            // put the returned object into a new array.
            if ( !( el instanceof Array ) )
                return new Array(RRS.util.Dom.get(el));
            
            // We were given an array and get will return an array, so we can
            // return that result as-is
            return RRS.util.Dom.get(el);
        },

        /**
         * <p>This function will return all of the elements with the specified class name.  You can specify where in the document structure to start the search by passing in a node.</p>
         *
         * <p>You can further filter by tag.</p>
         *
         * <p>COMPATIBILITY NOTE: You *must* specify a tag if you want it to work in Internet Explorer 5.5. This version does not support the asterisk as a wildcard in the getElementsByTagName function, which is used if there is no tag specified in the arguments. The user will not receive an error, just nothing will happen.</p>
         *
         * <p>This was taken from <a href="http://www.dustindiaz.com/getelementsbyclass/">Dustin Diaz's site</a>.</p>
         * @method getElementsByClass
         * @param searchClass {String} The name of the CSS class to look for
         * @param rootNode {Object} The node to start from (default: document)
         * @param tag {String} The element tag to filter by
         * @return {Array} An array of references to the elements that match the parameters passed in
        */
        getElementsByClass: function(searchClass,rootNode,tag)
        {
            var classElements = new Array();
            var els, elsLen, pattern, i, j;
                
            if ( typeof rootNode == 'undefined' || !rootNode )
                rootNode = document;
    
            if ( tag == null )
                tag = '*';
    
            els = rootNode.getElementsByTagName(tag);
            elsLength = els.length;
            pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
            for ( i = 0, j = 0; i < elsLength; i++ )
            {
                if ( pattern.test(els[i].className) )
                {
                    classElements[j] = els[i];
                    j++;
                }
            }
        
            return classElements;
        },

        /** 
         * Normalizes currentStyle and ComputedStyle; this allows you one call to get inline/computed style plus style from an external stylesheet
         * @method getStyle
         * @param el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElement objects
         * @param property A string containing the style property whose value is returned
         * @param noinline {Boolean} There may be times where you don't want the inline style of the element, since this is where styles are programatically set.  Set this to true if that is the case
         * @return A string with the current value of the style property for the element(s) or an array of them
        */
        getStyle: function(el,property,noinline)
        {           
            var camel,hypen,elements,computed,result,value;
            var dv = document.defaultView;
            
            // Make sure that this property is in the cache so we
            // only have to convert to camel/hyphen once
            if ( !property_cache[property] )
                cacheConvertedProperties(property);
                
            camel = property_cache[property]["camel"];
            hyphen = property_cache[property]["hyphen"];

            // Guarantee that we have an array of HTMLElement objects
            elements = RRS.util.Dom.getArray(el);

            // Get the style for each element and add it to 
            // the results array
            results = new Array();
            for ( i = 0, j = elements.length; i < j; i++ )
            {   
                value = "";
                el = elements[i];
            
                if ( !noinline && el.style && el.style[camel] )
                    value = el.style[camel];

                else if ( el.currentStyle && el.currentStyle[camel] )
                    value = el.currentStyle[camel];

                else if ( dv && dv.getComputedStyle )
                {
                    computed = dv.getComputedStyle(el,'');

                    if ( computed && computed.getPropertyValue(hyphen) )
                        value = computed.getPropertyValue(hyphen);
                }

                results[results.length] = value;
            }

            // If there is only one result, then we'll return
            // just that.  Otherwise, we'll return the array.
            if ( results.length > 1 )
                return results;
            else
                return results[0];
        },  
    
        /**
         * Wrapper for setting style properties of HTMLElements.  Caches the original value for the property before changing it.
         * @method setStyle
         * @param el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @param property A string with the style property to be set
         * @param val A string with the new value to apply to the given property
        */
        setStyle: function(el,property,val)
        {   
            var camel,elements,i,len;
        
            // Cache the property 
            if ( !property_cache[property] )
                cacheConvertedProperties(property);
            
            camel = property_cache[property]['camel'];
            
            // Guarantee that we have an array of HTMLElement objects
            elements = RRS.util.Dom.getArray(el);
            
            for ( i = 0, len = elements.length; i < len; i++ )
            {       
                el = elements[i];
                
                // Sanity check!  If the element doesn't exist, silently 
                // skip it.
                if ( !el )
                    continue;
                    
                if ( !cachedStyle(el,camel) )
                    cacheOriginalStyle(el,camel);
                
                el.style[camel] = val;
            }
        },
    
        /**
         * This method is meant to be called after altering an HTMLElement's style properties via setStyle.  It will get the original style out of the cache and reset the element's property to it.
         * @method revertStyle
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @param property {String} The style property to be reset
        */
        revertStyle: function(el,property)
        {
            var camel,elements,i,len,origVal;
        
            if ( !property_cache[property] )
                cacheConvertedProperties(property);
        
            camel = property_cache[property]['camel'];
        
            // Guarantee that we have an array of HTMLElement objects
            elements = RRS.util.Dom.getArray(el);
        
            for ( i = 0, len = elements.length; i < len; i++ )
            {
                el = elements[i];
                origVal = cachedStyle(el,camel);
                el.style[camel] = origVal;
            }
        },
    
        /**
         * Returns an array with all of the class names of the element
         * @method getClassNames
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @return {Array} An array of strings; all of the class names for the element or null if there are none
        */
        getClassNames: function(el)
        {
            return RRS.util.Dom.get(el).className.split(" ");
        },
    
        /**
         * Adds the class name specified to the other class names the element already has
         * @method addClassName
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @param className {String} The name of the class to add
        */
        addClassName: function(el,className)
        {
            var curNames,elements,i,len,anEl;
            
            // Make sure we have an array
            elements = RRS.util.Dom.getArray(el);
            
            for ( i = 0, len = elements.length; i < len; i++ )
            {
                anEl = elements[i];
                
                curNames = RRS.util.Dom.getClassNames(anEl);

                // Only add it in if it isn't already there
                if ( curNames.indexOf(className) == -1 )
                    anEl.className = curNames.concat(className).join(" ");
            }
        },
    
        /**
         * Removes the class name from the class names of the element
         * @method removeClassName
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @param className {String} The class name to remove
        */
        removeClassName: function(el,className)
        {
            var curNames,index,els,i,len,anEl;
            
            // Guarantee an array
            els = RRS.util.Dom.getArray(el);
            
            for ( i = 0, len = els.length; i < len; i++ )
            {
                anEl = els[i];
                
                curNames = RRS.util.Dom.getClassNames(anEl);
                index = curNames.indexOf(className);
                
                // Only attempt to remove it if it exists in the first place!
                if ( index != -1 )
                {
                    curNames.splice(index,1);
                    anEl.className = curNames.join(" ");
                }
            }
        },

        /**
         * Sets the element's display to "" so that it's display property will revert back to what it was specified as previously, either in the external stylesheet or the browser's default stylesheet; note that if the element's display was set to "none", that this might actually hide it.  Kinda confusing, but I can't think of a good way around that just yet.
         * @method show
        * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
        * @param newval {String} (optional) Allows you to specify a new value for the display property, either "block" or "inline"; this is necessary for elements that start out hidden because we don't know what to set the inline display value to.
        */
        show: function(el,newval)
        {
            var i,len;
            
            // Make sure we have an array of DOM references
            el = RRS.util.Dom.getArray(el);
            for ( i = 0, len = el.length; i < len; i++ )
            {
                el[i].style.display = (newval) ? newval : '';
            }
        },

        /** 
         * Sets the element's display to "none"
         * @method hide
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
        */
        hide: function(el)
        {
            var i,len;
            
            el = RRS.util.Dom.getArray(el);
            for ( i = 0, len = el.length; i < len; i++ )
            {
                el[i].style.display = "none";
            }
        },
        
        /**
         * Checks to see if the element is visible or not and then calls either RRS.util.Dom.show or RRS.util.Dom.hide
         * @method toggle
         * @param el {String/HTMLElement/Array} Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
         * @param newval {String} (optional) An optional value for the display attribute to be passed to RRS.util.Dom.show
        */
        toggle: function(el,newval)
        {
            var i, len, visible;
            
            el = RRS.util.Dom.getArray(el);
            for ( i = 0, len = el.length; i < len; i++ )
            {
                visible = ( RRS.util.Dom.getStyle(el[i],"display") == "none" ) ? false : true;
                if ( visible )
                    RRS.util.Dom.hide(el[i]);
                else
                    RRS.util.Dom.show(el[i],newval);
            }
        },
        
        /**
         * Will update the text in the passed-in element with newText, taking browser compatibility into account
         * @method updateText
         * @param el {String/HTMLElement} Accepts a string to use as an ID or an actual DOM reference
         * @param newText {String} The new text for the element
        */
        updateText: function(el,newText)
        {   
            el = $(el);
            if ( el.innerText )
                el.innerText = newText;
            else
                el.textContent = newText;
        },
        
        /**
         * Returns the text inside the element, taking browser compatibility into account
         * @method elementText
         * @param el {String/HTMLElement} Accepts a string to use as an ID or an actual DOM reference
         * @return {String} The text inside the element
        */
        elementText: function(el)
        {           
            el = $(el);
            return ( el.innerText ) ? el.innerText : el.textContent;
        },
        
        /**
         * <p>This is a workaround for stupid IE.  The default display value for a table row should be "table-row", however this is not a supported display value in IE.  In IE, the default display value is "block".  This is a supported value in other browsers, but you end up with some really funky results.  Therefore, when we need to display a table row that is by default hidden, we need to get the correct display value.  Stupid, huh?</p>
         *
         * <p>This method will create a new tr element and get the display value for it.  It doesn't insert the element into the document or anything.  It then caches the value it gets in _rowDisplay so that we don't need to get it again.</p>
         * @method defaultRowDisplay
        */
        defaultRowDisplay: function()
        {
            var body,trEl;
            
            if ( _rowDisplay == "" )
            {
                body = document.getElementsByTagName("body")[0];
                trEl = document.createElement("tr");
                body.appendChild(trEl);
                _rowDisplay = RRS.util.Dom.getStyle(trEl,"display");
                body.removeChild(trEl);
            }
            
            return _rowDisplay;
        },
        
        /**
         * This function will add the passed in function to the Window's onload event.  Use this instead of adding an onload to the body element in the XHTML.  The window's onload is called after the page has loaded, so you'll get access to all of the DOM objects.
         * @method addWindowOnload
         * @param func This is a function to execute in the window's onload event.  It will be added in along with any other existing functions.  If this is not a function, then nothing will change.
        */
        addWindowOnload: function(func)
        {
            var         oldOnload;
            
            // Check to see if we were passed in a function. If not, we'll just return
            if ( typeof func != "function" )
                return;
            
            // Hold onto the old onload value, if any
            oldOnload = window.onload;
            
            // Was there already a function to execute on loading?
            if ( typeof oldOnload != "function" )
                // Nope, just add this one function . . . 
                window.onload = func;
                
            else
            {
                // There are other functions to be called, so we'll tack
                // ours onto the back.
                window.onload = function()
                {
                    if ( oldOnload )
                        oldOnload();
                    func();
                };
            }
        },
        
        /**
         * <p>This method simulates a lightbox effect by adding in a div with an id of 'lightbox' as the first element in the body, if it isn't already in the DOM.  If it is, then it is removed. </p>
         * <p>The CSS in main.css styles the #lightbox element so that it is above all content, effectively creating a modal window.  Make sure that you are putting content over the lightbox layer so that the user can do something, otherwise they will be at a dead-end.</p>
          * @method toggleLightbox
        */
        toggleLightbox: function()
        {
            var         bodyEl, lightboxEl;
            
            bodyEl = document.getElementsByTagName("body")[0];
            lightboxEl = $('lightbox');
            
            if ( !lightboxEl )
            {
                lightboxEl = document.createElement('div');
                lightboxEl.id = 'lightbox';
                bodyEl.insertBefore(lightboxEl,bodyEl.firstChild);
            }
            else
            {
                lightboxEl = $('lightbox');
                bodyEl.removeChild(lightboxEl);
            }
        },
        
        /**
        * This is here to abstract out differences in DOM implementation between browsers.  It will return the height, in pixels, of the current browser window or viewport
         * @method windowHeight
        */
        windowHeight: function()
        {
            if ( window.innerHeight )
            {
                // alert("window.innerHeight = " + window.innerHeight);
                return window.innerHeight;
            }
            else if ( document.documentElement && document.documentElement.clientWidth )
            {
                // alert("document.documentElement.clientHeight = " + document.documentElement.clientHeight);
                return document.documentElement.clientHeight;
            }
            else if ( document.body )
            {
                // alert("document.body.clientHeight = " + document.body.clientHeight);
                return document.body.clientHeight;
            }
        }, 
        
        /**
         * This is here to abstract out differences in DOM implementation between browsers.  It will return the width, in pixels, of the current browser window or viewport
          *  @method windowWidth
        */
        windowWidth: function()
        {
            return document.all ? document.body.clientWidth : window.innerWidth;
        },
        
        /**
         * This will set the left and top CSS attributes for the element passed in so that it appears in the center of the window.
         * @method centerElementInWindow
         * @param el {String/HTMLElement} Accepts a string to use as an ID or an actual DOM reference
        */
        centerElementInWindow: function(el,boxWidth,boxHeight)
        {
            var         xpos, ypos, xposString, yposString;
            
            xpos = ( RRS.util.Dom.windowWidth() - boxWidth ) * 0.5;
            ypos = ( RRS.util.Dom.windowHeight() - boxHeight ) * 0.5;
            xposString = xpos.toString() + 'px';
            yposString = ypos.toString() + 'px';
            RRS.util.Dom.setStyle(el,"left",xposString);
            RRS.util.Dom.setStyle(el,"top",yposString);
        },
        
        /**
         * <p>Insert an element into the DOM directly after another element already there.</p>
         * <p>Special thanks to Jeremy Keith (this was in his book 'DOM Scripting')</p>
         * @method insertAfter
         * @param newEl {String/HTMLElement} The element that needs to be inserted into the DOM
         * @param targetEl {String/HTMLElement} The element behind which to insert newEl
        */
        insertAfter: function(newEl,targetEl)
        {
            var parent;
            
            newEl = $(newEl);
            targetEl = $(targetEl);
            
            parent = targetEl.parentNode;
            
            if ( parent.lastChild == targetEl )
                parent.appendChild(newEl);
            else
                parent.insertBefore(newEl, targetEl.nextSibling);
        }

    };
}(); // self invoke

/**
 * This function will open a separate window (popup) using the parameters passed in
 * @class popWindow
 * @namespace RRS.util
 * @param {String} url A string containing the URL to display in the new window
 * @param {int} width The width in pixels of the new window
 * @param {int} height The height in pixels of the new window
 * @param {String} name A string containing the name for the new window.
*/
RRS.util.popWindow = function(url,name,width,height,opener)
{   
    var newWin;

    if ( name === "undefined" )
        name = "";

    newWin = window.open(url,name,"scrollbars=yes,width=" + width + ",height=" + height + ",resizable=yes");
    
    // If there is an opener defined, then use it
    if ( opener )
        newWin.opener = opener;
};

// Make a shortcut
if ( !window.$ ) window.$ = RRS.util.Dom.get;
if ( !window.$c ) window.$c = RRS.util.Dom.getElementsByClass;

if ( !window.height ) window.height = RRS.util.Dom.windowHeight;
if ( !window.width ) window.width = RRS.util.Dom.windowWidth;