// BusyBoxDotNet. Web control library for ASP.NET to enhance user experience.
// Copyright (C) 2006  Simone Busoli, simone_b@tin.it
// Refer to Readme.txt and LGLP.txt for further details

var BBDN = function() 
{
	return { // funny, placing the { bracket on a new line gives error!
		
		util: {},
     
		core: {}
    };

} ();
/*
Copyright (c) 2006 Yahoo! Inc. All rights reserved.
version 0.9.0
*/

/**
 * @class The Yahoo global namespace
 */
var YAHOO = function() {

    return {

        /**
         * Yahoo presentation platform utils namespace
         */
        util: {},

        /**
         * Yahoo presentation platform widgets namespace
         */
        widget: {},

        /**
         * Yahoo presentation platform examples namespace
         */
        example: {},

        /**
         * Returns the namespace specified and creates it if it doesn't exist
         *
         * YAHOO.namespace("property.package");
         * YAHOO.namespace("YAHOO.property.package");
         *
         * Either of the above would create YAHOO.property, then
         * YAHOO.property.package
         *
         * @param  {String} sNameSpace String representation of the desired
         *                             namespace
         * @return {Object}            A reference to the namespace object
         */
        namespace: function( sNameSpace ) {

            if (!sNameSpace || !sNameSpace.length) {
                return null;
            }

            var levels = sNameSpace.split(".");

            var currentNS = YAHOO;

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

            return currentNS;

        }
    };

} ();
/*
Copyright (c) 2006 Yahoo! Inc. All rights reserved.
version 0.9.0
*/

/**
 * @class Provides helper methods for DOM elements.
 */
YAHOO.util.Dom = new function() {

   /**
    * Returns an HTMLElement reference
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @return {HTMLElement} A DOM reference to an HTML element.
    */
   this.get = function(el) {
      if (typeof el == 'string') { // accept object or id
         el = document.getElementById(el);
      }

      return el;
   };

   /**
    * Normalizes currentStyle and ComputedStyle.
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @param {String} property The style property whose value is returned.
    * @return {String} The current value of the style property.
    */
   this.getStyle = function(el, property) {
      var value = null;
      var dv = document.defaultView;

      el = this.get(el);

      if (property == 'opacity' && el.filters) {// IE opacity
         value = 1;
         try {
            value = el.filters.item('DXImageTransform.Microsoft.Alpha').opacity / 100;
         } catch(e) {
            try {
               value = el.filters.item('alpha').opacity / 100;
            } catch(e) {}
         }
      }
      else if (el.style[property]) {
         value = el.style[property];
      }
      else if (el.currentStyle && el.currentStyle[property]) {
         value = el.currentStyle[property];
      }
      else if ( dv && dv.getComputedStyle )
      {  // convert camelCase to hyphen-case

         var 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);
            }
         }

         if (dv.getComputedStyle(el, '').getPropertyValue(converted)) {
            value = dv.getComputedStyle(el, '').getPropertyValue(converted);
         }
      }

      return value;
   };

   /**
    * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @param {String} property The style property to be set.
    * @param {String} val The value to apply to the given property.
    */
   this.setStyle = function(el, property, val) {
      el = this.get(el);
      switch(property) {
         case 'opacity' :
            if (el.filters) {
               el.style.filter = 'alpha(opacity=' + val * 100 + ')';

               if (!el.currentStyle.hasLayout) {
                  el.style.zoom = 1;
               }
            } else {
               el.style.opacity = val;
               el.style['-moz-opacity'] = val;
               el.style['-khtml-opacity'] = val;
            }
            break;
         default :
            el.style[property] = val;
      }
   };

   /**
    * Gets the current position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    */
   this.getXY = function(el) {
      el = this.get(el);

      // has to be part of document to have pageXY
      if (el.parentNode === null || this.getStyle(el, 'display') == 'none') {
         return false;
      }

      /**
       * Position of the html element (x, y)
       * @private
       * @type Array
       */
      var parent = null;
      var pos = [];
      var box;

      if (el.getBoundingClientRect) { // IE
         box = el.getBoundingClientRect();
         var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
         var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;

         return [box.left + scrollLeft, box.top + scrollTop];
      }
      else if (document.getBoxObjectFor) { // gecko
         box = document.getBoxObjectFor(el);
         pos = [box.x, box.y];
      }
      else { // safari/opera
         pos = [el.offsetLeft, el.offsetTop];
         parent = el.offsetParent;
         if (parent != el) {
            while (parent) {
               pos[0] += parent.offsetLeft;
               pos[1] += parent.offsetTop;
               parent = parent.offsetParent;
            }
         }

         // opera & (safari absolute) incorrectly account for body offsetTop
         var ua = navigator.userAgent.toLowerCase();
         if (
            ua.indexOf('opera') != -1
            || ( ua.indexOf('safari') != -1 && this.getStyle(el, 'position') == 'absolute' )
         ) {
            pos[1] -= document.body.offsetTop;
         }
      }

      if (el.parentNode) { parent = el.parentNode; }
      else { parent = null; }

      while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML') {
         pos[0] -= parent.scrollLeft;
         pos[1] -= parent.scrollTop;

         if (parent.parentNode) { parent = parent.parentNode; }
         else { parent = null; }
      }

      return pos;
   };

   /**
    * Gets the current X position of an element based on page coordinates.  The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    */
   this.getX = function(el) {
      return this.getXY(el)[0];
   };

   /**
    * Gets the current Y position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    */
   this.getY = function(el) {
      return this.getXY(el)[1];
   };

   /**
    * Set the position of an html element in page coordinates, regardless of how the element is positioned.
    * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @param {array} pos Contains X & Y values for new position (coordinates are page-based)
    */
   this.setXY = function(el, pos, noRetry) {
      el = this.get(el);
      var pageXY = YAHOO.util.Dom.getXY(el);
      if (pageXY === false) { return false; } // has to be part of doc to have pageXY

      if (this.getStyle(el, 'position') == 'static') { // default to relative
         this.setStyle(el, 'position', 'relative');
      }

      var delta = [
         parseInt( YAHOO.util.Dom.getStyle(el, 'left'), 10 ),
         parseInt( YAHOO.util.Dom.getStyle(el, 'top'), 10 )
      ];

      if ( isNaN(delta[0]) ) { delta[0] = 0; } // defalts to 'auto'
      if ( isNaN(delta[1]) ) { delta[1] = 0; }

      if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
      if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }

      var newXY = this.getXY(el);

      // if retry is true, try one more time if we miss
      if (!noRetry && (newXY[0] != pos[0] || newXY[1] != pos[1]) ) {
         this.setXY(el, pos, true);
      }

      return true;
   };

   /**
    * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
    * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @param {Int} x to use as the X coordinate.
    */
   this.setX = function(el, x) {
      return this.setXY(el, [x, null]);
   };

   /**
    * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
    * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @param {Int} Value to use as the Y coordinate.
    */
   this.setY = function(el, y) {
      return this.setXY(el, [null, y]);
   };

   /**
    * Returns the region position of the given element.
    * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
    * @param {String | HTMLElement} Accepts either a string to use as an ID for getting a DOM reference, or an actual DOM reference.
    * @return {Region} A Region instance containing "top, left, bottom, right" member data.
    */
   this.getRegion = function(el) {
      el = this.get(el);
      return new YAHOO.util.Region.getRegion(el);
   };

   /**
    * Returns the width of the client (viewport).
    * @return {Int} The width of the viewable area of the page.
    */
   this.getClientWidth = function() {
      return (
         document.documentElement.offsetWidth
         || document.body.offsetWidth
      );
   };

   /**
    * Returns the height of the client (viewport).
    * @return {Int} The height of the viewable area of the page.
    */
   this.getClientHeight = function() {
      return (
         self.innerHeight
         || document.documentElement.clientHeight
         || document.body.clientHeight
      );
   };
};

/**
 * @class A region is a representation of an object on a grid.  It is defined
 * by the top, right, bottom, left extents, so is rectangular by default.  If
 * other shapes are required, this class could be extended to support it.
 *
 * @param {int} t the top extent
 * @param {int} r the right extent
 * @param {int} b the bottom extent
 * @param {int} l the left extent
 * @constructor
 */
YAHOO.util.Region = function(t, r, b, l) {

    /**
     * The region's top extent
     * @type int
     */
    this.top = t;

    /**
     * The region's right extent
     * @type int
     */
    this.right = r;

    /**
     * The region's bottom extent
     * @type int
     */
    this.bottom = b;

    /**
     * The region's left extent
     * @type int
     */
    this.left = l;
};

/**
 * Returns true if this region contains the region passed in
 *
 * @param  {Region}  region The region to evaluate
 * @return {boolean}        True if the region is contained with this region,
 *                          else false
 */
YAHOO.util.Region.prototype.contains = function(region) {
    return ( region.left   >= this.left   &&
             region.right  <= this.right  &&
             region.top    >= this.top    &&
             region.bottom <= this.bottom    );
};

/**
 * Returns the area of the region
 *
 * @return {int} the region's area
 */
YAHOO.util.Region.prototype.getArea = function() {
    return ( (this.bottom - this.top) * (this.right - this.left) );
};

/**
 * Returns the region where the passed in region overlaps with this one
 *
 * @param  {Region} region The region that intersects
 * @return {Region}        The overlap region, or null if there is no overlap
 */
YAHOO.util.Region.prototype.intersect = function(region) {
    var t = Math.max( this.top,    region.top    );
    var r = Math.min( this.right,  region.right  );
    var b = Math.min( this.bottom, region.bottom );
    var l = Math.max( this.left,   region.left   );

    if (b >= t && r >= l) {
        return new YAHOO.util.Region(t, r, b, l);
    } else {
        return null;
    }
};

/**
 * Returns the region representing the smallest region that can contain both
 * the passed in region and this region.
 *
 * @param  {Region} region The region that to create the union with
 * @return {Region}        The union region
 */
YAHOO.util.Region.prototype.union = function(region) {
    var t = Math.min( this.top,    region.top    );
    var r = Math.max( this.right,  region.right  );
    var b = Math.max( this.bottom, region.bottom );
    var l = Math.min( this.left,   region.left   );

    return new YAHOO.util.Region(t, r, b, l);
};

/**
 * toString
 * @return string the region properties
 */
YAHOO.util.Region.prototype.toString = function() {
    return ( "Region {" +
             "  t: "    + this.top    +
             ", r: "    + this.right  +
             ", b: "    + this.bottom +
             ", l: "    + this.left   +
             "}" );
};

/**
 * Returns a region that is occupied by the DOM element
 *
 * @param  {HTMLElement} el The element
 * @return {Region}         The region that the element occupies
 * @static
 */
YAHOO.util.Region.getRegion = function(el) {
    var p = YAHOO.util.Dom.getXY(el);

    var t = p[1];
    var r = p[0] + el.offsetWidth;
    var b = p[1] + el.offsetHeight;
    var l = p[0];

    return new YAHOO.util.Region(t, r, b, l);
};

/////////////////////////////////////////////////////////////////////////////


/**
 * @class
 *
 * A point is a region that is special in that it represents a single point on
 * the grid.
 *
 * @param {int} x The X position of the point
 * @param {int} y The Y position of the point
 * @constructor
 * @extends Region
 */
YAHOO.util.Point = function(x, y) {
    /**
     * The X position of the point
     * @type int
     */
    this.x      = x;

    /**
     * The Y position of the point
     * @type int
     */
    this.y      = y;

    this.top    = y;
    this.right  = x;
    this.bottom = y;
    this.left   = x;
};

YAHOO.util.Point.prototype = new YAHOO.util.Region();

/*
Copyright (c) 2006 Yahoo! Inc. All rights reserved.
version 0.9.0
*/

/**
 * @class The CustomEvent class lets you define events for your application
 * that can be subscribed to by one or more independent component.
 * @param {String} type The type of event, which is passed to the callback
 *                 when the event fires
 * @param {Object} oScope The context the event will fire from.  "this" will
 *                 refer to this object in the callback.  Default value:
 *                 the window object.  The listener can override this.
 * @constructor
 */
YAHOO.util.CustomEvent = function(type, oScope) {
    /**
     * The type of event, returned to subscribers when the event fires
     * @type string
     */
    this.type = type;

    /**
     * The scope the the event will fire from.  Defaults to the window obj
     * @type object
     */
    this.scope = oScope || window;

    /**
     * The subscribers to this event
     * @type array
     */
    this.subscribers = [];

    // Register with the event utility for automatic cleanup.  Made optional
    // so that CustomEvent can be used independently of pe.event
    if (YAHOO.util["Event"]) {
        YAHOO.util.Event.regCE(this);
    }
};

YAHOO.util.CustomEvent.prototype = {
    /**
     * Subscribes the caller to this event
     * @param {Function} fn       The function to execute
     * @param {Object}   obj      An object to be passed along when the event fires
     * @param {boolean}  bOverride If true, the obj passed in becomes the execution
     *                            scope of the listener
     */
    subscribe: function(fn, obj, bOverride) {
        this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, bOverride) );
    },

    /**
     * Unsubscribes the caller from this event
     * @param {Function} fn  The function to execute
     * @param {Object}   obj An object to be passed along when the event fires
     * @return {boolean} True if the subscriber was found and detached.
     */
    unsubscribe: function(fn, obj) {
        var found = false;
        for (var i=0; i<this.subscribers.length; ++i) {
            var s = this.subscribers[i];
            if (s && s.contains(fn, obj)) {
                this._delete(i);
                found = true;
            }
        }

        return found;
    },

    /**
     * Notifies the subscribers.  The callback functions will be executed
     * from the scope specified when the event was created, and with the following
     * parameters:
     *   <pre>
     *   - The type of event
     *   - All of the arguments fire() was executed with as an array
     *   - The custom object (if any) that was passed into the subscribe() method
     *   </pre>
     *
     * @param {Array} an arbitrary set of parameters to pass to the handler
     */
    fire: function() {
        for (var i=0; i<this.subscribers.length; ++i) {
            var s = this.subscribers[i];
            if (s) {
                var scope = (s.override) ? s.obj : this.scope;
                s.fn.call(scope, this.type, arguments, s.obj);
            }
        }
    },

    /**
     * Removes all listeners
     */
    unsubscribeAll: function() {
        for (var i=0; i<this.subscribers.length; ++i) {
            this._delete(i);
        }
    },

    /**
     * @private
     */
    _delete: function(index) {
        var s = this.subscribers[index];
        if (s) {
            delete s.fn;
            delete s.obj;
        }

        delete this.subscribers[index];
    }
};

/////////////////////////////////////////////////////////////////////

/**
 * @class
 * @param {Function} fn       The function to execute
 * @param {Object}   obj      An object to be passed along when the event fires
 * @param {boolean}  bOverride If true, the obj passed in becomes the execution
 *                            scope of the listener
 * @constructor
 */
YAHOO.util.Subscriber = function(fn, obj, bOverride) {
    /**
     * The callback that will be execute when the event fires
     * @type function
     */
    this.fn = fn;

    /**
     * An optional custom object that will passed to the callback when
     * the event fires
     * @type object
     */
    this.obj = obj || null;

    /**
     * The default execution scope for the event listener is defined when the
     * event is created (usually the object which contains the event).
     * By setting override to true, the execution scope becomes the custom
     * object passed in by the subscriber
     * @type boolean
     */
    this.override = (bOverride);
};

/**
 * Returns true if the fn and obj match this objects properties.
 * Used by the unsubscribe method to match the right subscriber.
 *
 * @param {Function} fn the function to execute
 * @param {Object} obj an object to be passed along when the event fires
 * @return {boolean} true if the supplied arguments match this
 *                   subscriber's signature.
 */
YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
    return (this.fn == fn && this.obj == obj);
};

/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */

// Only load this library once.  If it is loaded a second time, existing
// events cannot be detached.
if (!YAHOO.util.Event) {

/**
 * The event utility provides functions to add and remove event listeners,
 * event cleansing.  It also tries to automatically remove listeners it
 * registers during the unload event.
 * @class
 * @constructor
 */
    YAHOO.util.Event = function() {

        /**
         * True after the onload event has fired
         * @type boolean
         * @private
         */
        var loadComplete =  false;

        /**
         * Cache of wrapped listeners
         * @type array
         * @private
         */
        var listeners = [];

        /**
         * Listeners that will be attached during the onload event
         * @type array
         * @private
         */
        var delayedListeners = [];

        /**
         * User-defined unload function that will be fired before all events
         * are detached
         * @type array
         * @private
         */
        var unloadListeners = [];

        /**
         * Cache of the custom events that have been defined.  Used for
         * automatic cleanup
         * @type array
         * @private
         */
        var customEvents = [];

        /**
         * Cache of DOM0 event handlers to work around issues with DOM2 events
         * in Safari
         * @private
         */
        var legacyEvents = [];

        /**
         * Listener stack for DOM0 events
         * @private
         */
        var legacyHandlers = [];

        return { // PREPROCESS

            /**
             * Element to bind, int constant
             * @type int
             */
            EL: 0,

            /**
             * Type of event, int constant
             * @type int
             */
            TYPE: 1,

            /**
             * Function to execute, int constant
             * @type int
             */
            FN: 2,

            /**
             * Function wrapped for scope correction and cleanup, int constant
             * @type int
             */
            WFN: 3,

            /**
             * Object passed in by the user that will be returned as a
             * parameter to the callback, int constant
             * @type int
             */
            SCOPE: 3,

            /**
             * Adjusted scope, either the element we are registering the event
             * on or the custom object passed in by the listener, int constant
             * @type int
             */
            ADJ_SCOPE: 4,

            /**
             * Safari detection is necessary to work around the preventDefault
             * bug that makes it so you can't cancel a href click from the
             * handler.  There is not a capabilities check we can use here.
             * @private
             */
            isSafari: (navigator.userAgent.match(/safari/gi)),

            /**
             * @private
             * IE detection needed to properly calculate pageX and pageY.
             * capabilities checking didn't seem to work because another
             * browser that does not provide the properties have the values
             * calculated in a different manner than IE.
             */
            isIE: (!this.isSafari && navigator.userAgent.match(/msie/gi)),

            /**
             * Appends an event handler
             *
             * @param {Object}   el        The html element to assign the
             *                             event to
             * @param {String}   sType     The type of event to append
             * @param {Function} fn        The method the event invokes
             * @param {Object}   oScope    An arbitrary object that will be
             *                             passed as a parameter to the handler
             * @param {boolean}  bOverride If true, the obj passed in becomes
             *                             the execution scope of the listener
             * @return {boolean} True if the action was successful or defered,
             *                        false if one or more of the elements
             *                        could not have the event bound to it.
             */
            addListener: function(el, sType, fn, oScope, bOverride) {

                // The el argument can be an array of elements or element ids.
                if ( this._isValidCollection(el)) {
                    var ok = true;
                    for (var i=0; i< el.length; ++i) {
                        ok = ( this.on(el[i],
                                       sType,
                                       fn,
                                       oScope,
                                       bOverride) && ok );
                    }
                    return ok;

                } else if (typeof el == "string") {
                    // If the el argument is a string, we assume it is
                    // actually the id of the element.  If the page is loaded
                    // we convert el to the actual element, otherwise we
                    // defer attaching the event until onload event fires

                    // check to see if we need to delay hooking up the event
                    // until after the page loads.
                    if (loadComplete) {
                        el = this.getEl(el);
                    } else {
                        // defer adding the event until onload fires
                        delayedListeners[delayedListeners.length] =
                            [el, sType, fn, oScope, bOverride];

                        return true;
                    }
                }

                // Element should be an html element or an array if we get
                // here.
                if (!el) {
                    return false;
                }

                // we need to make sure we fire registered unload events
                // prior to automatically unhooking them.  So we hang on to
                // these instead of attaching them to the window and fire the
                // handles explicitly during our one unload event.
                if ("unload" == sType && oScope !== this) {
                    unloadListeners[unloadListeners.length] =
                            [el, sType, fn, oScope, bOverride];
                    return true;
                }


                // if the user chooses to override the scope, we use the custom
                // object passed in, otherwise the executing scope will be the
                // HTML element that the event is registered on
                var scope = (bOverride) ? oScope : el;

                // wrap the function so we can return the oScope object when
                // the event fires;
                var wrappedFn = function(e) {
                        return fn.call(scope, YAHOO.util.Event.getEvent(e),
                                oScope);
                    };

                var li = [el, sType, fn, wrappedFn, scope];
                var index = listeners.length;
                // cache the listener so we can try to automatically unload
                listeners[index] = li;

                if (this.useLegacyEvent(el, sType)) {
                    var legacyIndex = this.getLegacyIndex(el, sType);
                    if (legacyIndex == -1) {

                        legacyIndex = legacyEvents.length;
                        // cache the signature for the DOM0 event, and
                        // include the existing handler for the event, if any
                        legacyEvents[legacyIndex] =
                            [el, sType, el["on" + sType]];
                        legacyHandlers[legacyIndex] = [];

                        el["on" + sType] =
                            function(e) {
                                YAHOO.util.Event.fireLegacyEvent(
                                    YAHOO.util.Event.getEvent(e), legacyIndex);
                            };
                    }

                    // add a reference to the wrapped listener to our custom
                    // stack of events
                    legacyHandlers[legacyIndex].push(index);

                // DOM2 Event model
                } else if (el.addEventListener) {
                    el.addEventListener(sType, wrappedFn, false);
                // Internet Explorer abstraction
                } else if (el.attachEvent) {
                    el.attachEvent("on" + sType, wrappedFn);
                }

                return true;

            },

            /**
             * Shorthand for YAHOO.util.Event.addListener
             * @type function
             */
            // on: this.addListener,

            /**
             * When using legacy events, the handler is routed to this object
             * so we can fire our custom listener stack.
             * @private
             */
            fireLegacyEvent: function(e, legacyIndex) {
                // alert("fireLegacyEvent " + legacyIndex);
                var ok = true;

                // var el = legacyEvents[YAHOO.util.Event.EL];

                /* this is not working because the property may get populated
                // fire the event we replaced, if it exists
                var origHandler = legacyEvents[2];
                alert(origHandler);
                if (origHandler && origHandler.call) {
                    var ret = origHandler.call(el, e);
                    ok = (ret);
                }
                */

                var le = legacyHandlers[legacyIndex];
                for (i=0; i < le.length; ++i) {
                    var index = le[i];
                    // alert(index);
                    if (index) {
                        var li = listeners[index];
                        var scope = li[this.ADJ_SCOPE];
                        var ret = li[this.WFN].call(scope, e);
                        ok = (ok && ret);
                        // alert(ok);
                    }
                }

                return ok;
            },

            /**
             * Returns the legacy event index that matches the supplied
             * signature
             * @private
             */
            getLegacyIndex: function(el, sType) {
                for (var i=0; i < legacyEvents.length; ++i) {
                    var le = legacyEvents[i];
                    if (le && le[0] == el && le[1] == sType) {
                        return i;
                    }
                }

                return -1;
            },

            /**
             * Logic that determines when we should automatically use legacy
             * events instead of DOM2 events.
             * @private
             */
            useLegacyEvent: function(el, sType) {

                return ( (!el.addEventListener && !el.attachEvent) ||
                                (sType == "click" && this.isSafari) );
            },

            /**
             * Removes an event handler
             *
             * @param {Object} el the html element or the id of the element to
             * assign the event to.
             * @param {String} sType the type of event to remove
             * @param {Function} fn the method the event invokes
             * @return {boolean} true if the unbind was successful, false
             * otherwise
             */
            removeListener: function(el, sType, fn) {

                // The el argument can be a string
                if (typeof el == "string") {
                    el = this.getEl(el);
                // The el argument can be an array of elements or element ids.
                } else if ( this._isValidCollection(el)) {
                    var ok = true;
                    for (var i=0; i< el.length; ++i) {
                        ok = ( this.removeListener(el[i], sType, fn) && ok );
                    }
                    return ok;
                }

                var cacheItem = null;
                var index = this._getCacheIndex(el, sType, fn);

                if (index >= 0) {
                    cacheItem = listeners[index];
                }

                if (!el || !cacheItem) {
                    return false;
                }


                if (el.removeEventListener) {
                    el.removeEventListener(sType, cacheItem[this.WFN], false);
                    // alert("adsf");
                } else if (el.detachEvent) {
                    el.detachEvent("on" + sType, cacheItem[this.WFN]);
                }

                // removed the wrapped handler
                delete listeners[index][this.WFN];
                delete listeners[index][this.FN];
                delete listeners[index];

                return true;

            },

            /**
             * Returns the event's target element
             * @param {Event} ev the event
             * @param {boolean} resolveTextNode when set to true the target's
             *                  parent will be returned if the target is a
             *                  text node
             * @return {HTMLElement} the event's target
             */
            getTarget: function(ev, resolveTextNode) {
                var t = ev.target || ev.srcElement;

                if (resolveTextNode && t && "#text" == t.nodeName) {
                    return t.parentNode;
                } else {
                    return t;
                }
            },

            /**
             * Returns the event's pageX
             * @param {Event} ev the event
             * @return {int} the event's pageX
             */
            getPageX: function(ev) {
                var x = ev.pageX;
                if (!x && 0 !== x) {
                    x = ev.clientX || 0;

                    if ( this.isIE ) {
                        x += this._getScrollLeft();
                    }
                }

                return x;
            },

            /**
             * Returns the event's pageY
             * @param {Event} ev the event
             * @return {int} the event's pageY
             */
            getPageY: function(ev) {
                var y = ev.pageY;
                if (!y && 0 !== y) {
                    y = ev.clientY || 0;

                    if ( this.isIE ) {
                        y += this._getScrollTop();
                    }
                }

                return y;
            },

            /**
             * Returns the event's related target
             * @param {Event} ev the event
             * @return {HTMLElement} the event's relatedTarget
             */
            getRelatedTarget: function(ev) {
                var t = ev.relatedTarget;
                if (!t) {
                    if (ev.type == "mouseout") {
                        t = ev.toElement;
                    } else if (ev.type == "mouseover") {
                        t = ev.fromElement;
                    }
                }

                return t;
            },

            /**
             * Returns the time of the event.  If the time is not included, the
             * event is modified using the current time.
             * @param {Event} ev the event
             * @return {Date} the time of the event
             */
            getTime: function(ev) {
                if (!ev.time) {
                    var t = new Date().getTime();
                    try {
                        ev.time = t;
                    } catch(e) {
                        // can't set the time property
                        return t;
                    }
                }

                return ev.time;
            },

            /**
             * Convenience method for stopPropagation + preventDefault
             * @param {Event} ev the event
             */
            stopEvent: function(ev) {
                this.stopPropagation(ev);
                this.preventDefault(ev);
            },

            /**
             * Stops event propagation
             * @param {Event} ev the event
             */
            stopPropagation: function(ev) {
                if (ev.stopPropagation) {
                    ev.stopPropagation();
                } else {
                    ev.cancelBubble = true;
                }
            },

            /**
             * Prevents the default behavior of the event
             * @param {Event} ev the event
             */
            preventDefault: function(ev) {
                if (ev.preventDefault) {
                    ev.preventDefault();
                } else {
                    ev.returnValue = false;
                }
            },

            /**
             * Returns the event, should not be necessary for user to call
             * @param {Event} the event parameter from the handler
             * @return {Event} the event
             */
            getEvent: function(e) {
                var ev = e || window.event;

                if (!ev) {
                    var c = this.getEvent.caller;
                    while (c) {
                        ev = c.arguments[0];
                        if (ev && Event == ev.constructor) {
                            break;
                        }
                        c = c.caller;
                    }
                }

                return ev;
            },

            /**
             * Returns the charcode for an event
             * @param {Event} ev the event
             * @return {int} the event's charCode
             */
            getCharCode: function(ev) {
                return ev.charCode || (ev.type == "keypress") ? ev.keyCode : 0;
            },

            /**
             * @private
             * Locating the saved event handler data by function ref
             */
            _getCacheIndex: function(el, sType, fn) {
                for (var i=0; i< listeners.length; ++i) {
                    var li = listeners[i];
                    if ( li                 &&
                         li[this.FN] == fn  &&
                         li[this.EL] == el  &&
                         li[this.TYPE] == sType ) {
                        return i;
                    }
                }

                return -1;
            },

            /**
             * We want to be able to use getElementsByTagName as a collection
             * to attach a group of events to.  Unfortunately, different
             * browsers return different types of collections.  This function
             * tests to determine if the object is array-like.  It will also
             * fail if the object is an array, but is empty.
             * @param o the object to test
             * @return {boolean} true if the object is array-like and populated
             */
            _isValidCollection: function(o) {
                // alert(o.constructor.toString())
                // alert(typeof o)

                return ( o                    && // o is something
                         o.length             && // o is indexed
                         typeof o != "string" && // o is not a string
                         !o.tagName           && // o is not an HTML element
                         !o.alert             && // o is not a window
                         typeof o[0] != "undefined" );

            },

            /**
             * @private
             * DOM element cache
             */
            elCache: {},

            /**
             * We cache elements bound by id because when the unload event
             * fires, we can no longer use document.getElementById
             * @private
             */
            getEl: function(id) {
                /*
                // this is a problem when replaced via document.getElementById
                if (! this.elCache[id]) {
                    try {
                        var el = document.getElementById(id);
                        if (el) {
                            this.elCache[id] = el;
                        }
                    } catch (er) {
                    }
                }
                return this.elCache[id];
                */

                return document.getElementById(id);
            },

            /**
             * Clears the element cache
             */
            clearCache: function() {
                for (i in this.elCache) {
                    delete this.elCache[i];
                }
            },

            /**
             * Called by CustomEvent instances to provide a handle to the
             * event * that can be removed later on.  Should be package
             * protected.
             * @private
             */
            regCE: function(ce) {
                customEvents.push(ce);
            },

            /**
             * @private
             * hook up any deferred listeners
             */
            _load: function(e) {
                loadComplete = true;
            },

            /**
             * Polling function that runs before the onload event fires,
             * attempting * to attach to DOM Nodes as soon as they are
             * available
             * @private
             */
            _tryPreloadAttach: function() {

                // keep trying until after the page is loaded.  We need to
                // check the page load state prior to trying to bind the
                // elements so that we can be certain all elements have been
                // tested appropriately
                var tryAgain = !loadComplete;

                for (var i=0; i < delayedListeners.length; ++i) {
                    var d = delayedListeners[i];
                    // There may be a race condition here, so we need to
                    // verify the array element is usable.
                    if (d) {

                        // el will be null if document.getElementById did not
                        // work
                        var el = this.getEl(d[this.EL]);

                        if (el) {
                            this.on(el, d[this.TYPE], d[this.FN],
                                    d[this.SCOPE], d[this.ADJ_SCOPE]);
                            delete delayedListeners[i];
                        }
                    }
                }

                if (tryAgain) {
                    setTimeout("YAHOO.util.Event._tryPreloadAttach()", 50);
                }
            },

            /**
             * Removes all listeners registered by pe.event.  Called
             * automatically during the unload event.
             */
            _unload: function(e, me) {
                for (var i=0; i < unloadListeners.length; ++i) {
                    var l = unloadListeners[i];
                    if (l) {
                        var scope = (l[this.ADJ_SCOPE]) ? l[this.SCOPE]: window;
                        l[this.FN].call(scope, this.getEvent(e), l[this.SCOPE] );
                    }
                }

                if (listeners && listeners.length > 0) {
                    for (i = 0; i < listeners.length; ++i) {
                        l = listeners[i];
                        if (l) {
                            this.removeListener(l[this.EL], l[this.TYPE],
                                    l[this.FN]);
                        }
                    }

                    this.clearCache();
                }

                for (i = 0; i < customEvents.length; ++i) {
                    customEvents[i].unsubscribeAll();
                    delete customEvents[i];
                }

                for (i = 0; i < legacyEvents.length; ++i) {
                    // dereference the element
                    delete legacyEvents[i][0];
                    // delete the array item
                    delete legacyEvents[i];
                }
            },

            /**
             * Returns scrollLeft
             * @private
             */
            _getScrollLeft: function() {
                return this._getScroll()[1];
            },

            /**
             * Returns scrollTop
             * @private
             */
            _getScrollTop: function() {
                return this._getScroll()[0];
            },

            /**
             * Returns the scrollTop and scrollLeft.  Used to calculate the
             * pageX and pageY in Internet Explorer
             * @private
             */
            _getScroll: function() {
                var dd = document.documentElement; db = document.body;
                if (dd && dd.scrollTop) {
                    return [dd.scrollTop, dd.scrollLeft];
                } else if (db) {
                    return [db.scrollTop, db.scrollLeft];
                } else {
                    return [0, 0];
                }
            }
        };
    } ();

    YAHOO.util.Event.on = YAHOO.util.Event.addListener;

    if (document && document.body) {
        YAHOO.util.Event._load();
    } else {
        YAHOO.util.Event.on(window, "load", YAHOO.util.Event._load,
                YAHOO.util.Event, true);
    }

    YAHOO.util.Event.on(window, "unload", YAHOO.util.Event._unload,
                YAHOO.util.Event, true);

    YAHOO.util.Event._tryPreloadAttach();

}


/*
Copyright (c) 2006 Yahoo! Inc. All rights reserved.
version 0.9.0
*/

/**
 *
 * Base class for animated DOM objects.
 * @class Base animation class that provides the interface for building animated effects.
 * <p>Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);</p>
 * @requires YAHOO.util.AnimMgr
 * @requires YAHOO.util.Easing
 * @requires YAHOO.util.Dom
 * @requires YAHOO.util.Event
 * @constructor
 * @param {HTMLElement | String} el Reference to the element that will be animated
 * @param {Object} attributes The attribute(s) to be animated.
 * Each attribute is an object with at minimum a "to" or "by" member defined.
 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
 * All attribute names use camelCase.
 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
 */

YAHOO.util.Anim = function(el, attributes, duration, method)
{
   if (el) {
      this.init(el, attributes, duration, method);
   }
};

YAHOO.util.Anim.prototype = {
   /**
    * Returns the value computed by the animation's "method".
    * @param {String} attribute The name of the attribute.
    * @param {Number} start The value this attribute should start from for this animation.
    * @param {Number} end  The value this attribute should end at for this animation.
    * @return {Number} The Value to be applied to the attribute.
    */
   doMethod: function(attribute, start, end) {
      return this.method(this.currentFrame, start, end - start, this.totalFrames);
   },

   /**
    * Applies a value to an attribute
    * @param {String} attribute The name of the attribute.
    * @param {Number} val The value to be applied to the attribute.
    * @param {String} unit The unit ('px', '%', etc.) of the value.
    */
   setAttribute: function(attribute, val, unit) {
      YAHOO.util.Dom.setStyle(this.getEl(), attribute, val + unit);
   },

   /**
    * Returns current value of the attribute.
    * @param {String} attribute The name of the attribute.
    * @return {Number} val The current value of the attribute.
    */
   getAttribute: function(attribute) {
      return parseFloat( YAHOO.util.Dom.getStyle(this.getEl(), attribute));
   },

   /**
    * Per attribute units that should be used by default.
    * @type {Object}
    */
   defaultUnits: {
      opacity: ' '
   },

   /**
    * The default unit to use for all attributes if not defined per attribute.
    * @type {String}
    */
   defaultUnit: 'px',

   /**
    * @param {HTMLElement | String} el Reference to the element that will be animated
    * @param {Object} attributes The attribute(s) to be animated.
    * Each attribute is an object with at minimum a "to" or "by" member defined.
    * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
    * All attribute names use camelCase.
    * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
    * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
    */
   init: function(el, attributes, duration, method) {

      /**
       * Whether or not the animation is running.
       * @private
       * @type {Boolen}
       */
      var isAnimated = false;

      /**
       * A Date object that is created when the animation begins.
       * @private
       * @type {Date}
       */
      var startTime = null;

      /**
       * A Date object that is created when the animation ends.
       * @private
       * @type {Date}
       */
      var endTime = null;

      /**
       * The number of frames this animation was able to execute.
       * @private
       * @type {Int}
       */
      var actualFrames = 0;

      /**
       * The attribute values that will be used if no "from" is supplied.
       * @private
       * @type {Object}
       */
      var defaultValues = {};

      /**
       * The element to be animated.
       * @private
       * @type {HTMLElement}
       */
      el = YAHOO.util.Dom.get(el);

      /**
       * The collection of attributes to be animated.
       * Each attribute must have at least a "to" or "by" defined in order to animate.
       * If "to" is supplied, the animation will end with the attribute at that value.
       * If "by" is supplied, the animation will end at that value plus its starting value.
       * If both are supplied, "to" is used, and "by" is ignored.
       * Optional additional member include "from" (the value the attribute should start animating from, defaults to current value), and "unit" (the units to apply to the values).
       * @type {Object}
       */
      this.attributes = attributes || {};

      /**
       * The length of the animation.  Defaults to "1" (second).
       * @type {Number}
       */
      this.duration = duration || 1;

      /**
       * The method that will provide values to the attribute(s) during the animation.
       * Defaults to "YAHOO.util.Easing.easeNone".
       * @type {Function}
       */
      this.method = method || YAHOO.util.Easing.easeNone;

      /**
       * Whether or not the duration should be treated as seconds.
       * Defaults to true.
       * @type {Boolean}
       */
      this.useSeconds = true; // default to seconds

      /**
       * The location of the current animation on the timeline.
       * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
       * @type {Int}
       */
      this.currentFrame = 0;

      /**
       * The total number of frames to be executed.
       * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
       * @type {Int}
       */
      this.totalFrames = YAHOO.util.AnimMgr.fps;


      /**
       * Returns a reference to the animated element.
       * @return {HTMLElement}
       */
      this.getEl = function() { return el; };


      /**
       * Sets the default value to be used when "from" is not supplied.
       * @param {String} attribute The attribute being set.
       * @param {Number} val The default value to be applied to the attribute.
       */
      this.setDefault = function(attribute, val) {
         if ( val == 'auto' ) { // if 'auto' set defaults for well known attributes, zero for others
            switch(attribute) {
               case'width':
                  val = el.clientWidth || el.offsetWidth; // computed width
                  break;
               case 'height':
                  val = el.clientHeight || el.offsetHeight; // computed height
                  break;
               case 'left':
                  if (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute') {
                     val = el.offsetLeft; // computed left
                  } else {
                     val = 0;
                  }
                  break;
               case 'top':
                  if (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute') {
                     val = el.offsetTop; // computed top
                  } else {
                     val = 0;
                  }
                  break;
               default:
                  val = 0;
            }
         }

         defaultValues[attribute] = val;
      };

      /**
       * Returns the default value for the given attribute.
       * @param {String} attribute The attribute whose value will be returned.
       */
      this.getDefault = function(attribute) {
         return defaultValues[attribute];
      };

      /**
       * Checks whether the element is currently animated.
       * @return {Boolean} current value of isAnimated.
       */
      this.isAnimated = function() {
         return isAnimated;
      };

      /**
       * Returns the animation start time.
       * @return {Date} current value of startTime.
       */
      this.getStartTime = function() {
         return startTime;
      };

      /**
       * Starts the animation by registering it with the animation manager.
       */
      this.animate = function() {
         this.onStart.fire();
         this._onStart.fire();

         this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration;
         YAHOO.util.AnimMgr.registerElement(this);

         // get starting values or use defaults
         var attributes = this.attributes;
         var el = this.getEl();
         var val;

         for (var attribute in attributes) {
            val = this.getAttribute(attribute);
            this.setDefault(attribute, val);
         }

         isAnimated = true;
         actualFrames = 0;
         startTime = new Date();
      };

      /**
       * Stops the animation.  Normally called by AnimMgr when animation completes.
       */
      this.stop = function() {
         this.currentFrame = 0;

         endTime = new Date();

         var data = {
            time: endTime,
            duration: endTime - startTime,
            frames: actualFrames,
            fps: actualFrames / this.duration
         };

         isAnimated = false;
         actualFrames = 0;

         this.onComplete.fire(data);
      };

      /**
       * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s).
       * @private
       */
      var onTween = function() {
         var start;
         var end = null;
         var val;
         var unit;
         var attributes = this['attributes'];

         for (var attribute in attributes) {
            unit = attributes[attribute]['unit'] || this.defaultUnits[attribute] || this.defaultUnit;

            if (typeof attributes[attribute]['from'] != 'undefined') {
               start = attributes[attribute]['from'];
            } else {
               start = this.getDefault(attribute);
            }

            // To beats by, per SMIL 2.1 spec
            if (typeof attributes[attribute]['to'] != 'undefined') {
               end = attributes[attribute]['to'];
            } else if (typeof attributes[attribute]['by'] != 'undefined') {
               end = start + attributes[attribute]['by'];
            }

            // if end is null, dont change value
            if (end !== null && typeof end != 'undefined') {

               val = this.doMethod(attribute, start, end);

               // negative not allowed for these (others too, but these are most common)
               if ( (attribute == 'width' || attribute == 'height' || attribute == 'opacity') && val < 0 ) {
                  val = 0;
               }

               this.setAttribute(attribute, val, unit);
            }
         }

         actualFrames += 1;
      };

      /**
       * Custom event that fires after onStart, useful in subclassing
       * @private
       */
      this._onStart = new YAHOO.util.CustomEvent('_onStart', this);

      /**
       * Custom event that fires when animation begins
       * Listen via subscribe method
       */
      this.onStart = new YAHOO.util.CustomEvent('start', this);

      /**
       * Custom event that fires between each frame
       * Listen via subscribe method
       */
      this.onTween = new YAHOO.util.CustomEvent('tween', this);

      /**
       * Custom event that fires after onTween
       * @private
       */
      this._onTween = new YAHOO.util.CustomEvent('_tween', this);

      /**
       * Custom event that fires when animation ends
       * Listen via subscribe method
       */
      this.onComplete = new YAHOO.util.CustomEvent('complete', this);

      this._onTween.subscribe(onTween);
   }
};

/**
 * @class Handles animation queueing and threading.
 * Used by Anim and subclasses.
 */
YAHOO.util.AnimMgr = new function() {
   /**
    * Reference to the animation Interval
    * @private
    * @type Int
    */
   var thread = null;

   /**
    * The current queue of registered animation objects.
    * @private
    * @type Array
    */
   var queue = [];

   /**
    * The number of active animations.
    * @private
    * @type Int
    */
   var tweenCount = 0;

   /**
    * Base frame rate (frames per second).
    * Arbitrarily high for better x-browser calibration (slower browsers drop more frames).
    * @type Int
    *
    */
   this.fps = 200;

   /**
    * Interval delay in milliseconds, defaults to fastest possible.
    * @type Int
    *
    */
   this.delay = 1;

   /**
    * Adds an animation instance to the animation queue.
    * All animation instances must be registered in order to animate.
    * @param {object} tween The Anim instance to be be registered
    */
   this.registerElement = function(tween) {
      if ( tween.isAnimated() ) { return false; }// but not if already animating

      queue[queue.length] = tween;
      tweenCount += 1;

      this.start();
   };

   /**
    * Starts the animation thread.
	 * Only one thread can run at a time.
    */
   this.start = function() {
      if (thread === null) { thread = setInterval(this.run, this.delay); }
   };

   /**
    * Stops the animation thread or a specific animation instance.
    * @param {object} tween A specific Anim instance to stop (optional)
    * If no instance given, Manager stops thread and all animations.
    */
   this.stop = function(tween) {
      if (!tween)
      {
         clearInterval(thread);
         for (var i = 0, len = queue.length; i < len; ++i) {
            if (queue[i].isAnimated()) {
               queue[i].stop();
            }
         }
         queue = [];
         thread = null;
         tweenCount = 0;
      }
      else {
         tween.stop();
         tweenCount -= 1;

         if (tweenCount <= 0) { this.stop(); }
      }
   };

   /**
    * Called per Interval to handle each animation frame.
    */
   this.run = function() {
      for (var i = 0, len = queue.length; i < len; ++i) {
         var tween = queue[i];
         if ( !tween || !tween.isAnimated() ) { continue; }

         if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
         {
            tween.currentFrame += 1;

            if (tween.useSeconds) {
               correctFrame(tween);
            }

            tween.onTween.fire();
            tween._onTween.fire();
         }
         else { YAHOO.util.AnimMgr.stop(tween); }
      }
   };

   /**
    * On the fly frame correction to keep animation on time.
    * @private
    * @param {Object} tween The Anim instance being corrected.
    */
   var correctFrame = function(tween) {
      var frames = tween.totalFrames;
      var frame = tween.currentFrame;
      var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
      var elapsed = (new Date() - tween.getStartTime());
      var tweak = 0;

      if (elapsed < tween.duration * 1000) { // check if falling behind
         tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
      } else { // went over duration, so jump to end
         tweak = frames - (frame + 1);
      }
      if (tweak > 0 && isFinite(tweak)) { // adjust if needed
         if (tween.currentFrame + tweak >= frames) {// dont go past last frame
            tweak = frames - (frame + 1);
         }

         tween.currentFrame += tweak;
      }
   };
};

/**
 *
 * @class Used to calculate Bezier splines for any number of control points.
 *
 */
YAHOO.util.Bezier = new function()
{
   /**
    * Get the current position of the animated element based on t.
    * @param {array} points An array containing Bezier points.
    * Each point is an array of "x" and "y" values (0 = x, 1 = y)
    * At least 2 points are required (start and end).
    * First point is start. Last point is end.
    * Additional control points are optional.
    * @param {float} t Basis for determining current position (0 < t < 1)
    * @return {object} An object containing int x and y member data
    */
   this.getPosition = function(points, t)
   {
      var n = points.length;
      var tmp = [];

      for (var i = 0; i < n; ++i){
         tmp[i] = [points[i][0], points[i][1]]; // save input
      }

      for (var j = 1; j < n; ++j) {
         for (i = 0; i < n - j; ++i) {
            tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
            tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
         }
      }

      return [ tmp[0][0], tmp[0][1] ];

   };
};

/**
 * @class Class for defining the acceleration rate and path of animations.
 */
YAHOO.util.Easing = new function() {

   /**
    * Uniform speed between points.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.easeNone = function(t, b, c, d) {
	return b+c*(t/=d);
   };

   /**
    * Begins slowly and accelerates towards end.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.easeIn = function(t, b, c, d) {
   	return b+c*((t/=d)*t*t);
   };

   /**
    * Begins quickly and decelerates towards end.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.easeOut = function(t, b, c, d) {
   	var ts=(t/=d)*t;
   	var tc=ts*t;
   	return b+c*(tc + -3*ts + 3*t);
   };

   /**
    * Begins slowly and decelerates towards end.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.easeBoth = function(t, b, c, d) {
   	var ts=(t/=d)*t;
   	var tc=ts*t;
   	return b+c*(-2*tc + 3*ts);
   };

   /**
    * Begins by going below staring value.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.backIn = function(t, b, c, d) {
   	var ts=(t/=d)*t;
   	var tc=ts*t;
   	return b+c*(-3.4005*tc*ts + 10.2*ts*ts + -6.2*tc + 0.4*ts);
   };

   /**
    * End by going beyond ending value.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.backOut = function(t, b, c, d) {
   	var ts=(t/=d)*t;
   	var tc=ts*t;
   	return b+c*(8.292*tc*ts + -21.88*ts*ts + 22.08*tc + -12.69*ts + 5.1975*t);
   };

   /**
    * Starts by going below staring value, and ends by going beyond ending value.
    * @param {Number} t Time value used to compute current value.
    * @param {Number} b Starting value.
    * @param {Number} c Delta between start and end values.
    * @param {Number} d Total length of animation.
    * @return {Number} The computed value for the current animation frame.
    */
   this.backBoth = function(t, b, c, d) {
   	var ts=(t/=d)*t;
   	var tc=ts*t;
   	return b+c*(0.402*tc*ts + -2.1525*ts*ts + -3.2*tc + 8*ts + -2.05*t);
   };
};

/**
 * @class Anim subclass for moving elements along a path defined by the "points" member of "attributes".  All "points" are arrays with x, y coordinates.
 * <p>Usage: <code>var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
 * @requires YAHOO.util.Anim
 * @requires YAHOO.util.AnimMgr
 * @requires YAHOO.util.Easing
 * @requires YAHOO.util.Bezier
 * @requires YAHOO.util.Dom
 * @requires YAHOO.util.Event
 * @constructor
 * @param {HTMLElement | String} el Reference to the element that will be animated
 * @param {Object} attributes The attribute(s) to be animated.
 * Each attribute is an object with at minimum a "to" or "by" member defined.
 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
 * All attribute names use camelCase.
 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
 */
YAHOO.util.Motion = function(el, attributes, duration, method) {
   if (el) {
      this.initMotion(el, attributes, duration, method);
   }
};

YAHOO.util.Motion.prototype = new YAHOO.util.Anim();

/**
 * Per attribute units that should be used by default.
 * Motion points default to 'px' units.
 * @type Object
 */
YAHOO.util.Motion.prototype.defaultUnits.points = 'px';

/**
 * Returns the value computed by the animation's "method".
 * @param {String} attribute The name of the attribute.
 * @param {Number} start The value this attribute should start from for this animation.
 * @param {Number} end  The value this attribute should end at for this animation.
 * @return {Number} The Value to be applied to the attribute.
 */
YAHOO.util.Motion.prototype.doMethod = function(attribute, start, end) {
   var val = null;

   if (attribute == 'points') {
      var translatedPoints = this.getTranslatedPoints();
      var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;

      if (translatedPoints) {
         val = YAHOO.util.Bezier.getPosition(translatedPoints, t);
      }

   } else {
      val = this.method(this.currentFrame, start, end - start, this.totalFrames);
   }

   return val;
};

/**
 * Returns current value of the attribute.
 * @param {String} attribute The name of the attribute.
 * @return {Number} val The current value of the attribute.
 */
YAHOO.util.Motion.prototype.getAttribute = function(attribute) {
   var val = null;

   if (attribute == 'points') {
      val = [ this.getAttribute('left'), this.getAttribute('top') ];
      if ( isNaN(val[0]) ) { val[0] = 0; }
      if ( isNaN(val[1]) ) { val[1] = 0; }
   } else {
      val = parseFloat( YAHOO.util.Dom.getStyle(this.getEl(), attribute) );
   }

   return val;
};

/**
 * Applies a value to an attribute
 * @param {String} attribute The name of the attribute.
 * @param {Number} val The value to be applied to the attribute.
 * @param {String} unit The unit ('px', '%', etc.) of the value.
 */
YAHOO.util.Motion.prototype.setAttribute = function(attribute, val, unit) {
   if (attribute == 'points') {
      YAHOO.util.Dom.setStyle(this.getEl(), 'left', val[0] + unit);
      YAHOO.util.Dom.setStyle(this.getEl(), 'top', val[1] + unit);
   } else {
      YAHOO.util.Dom.setStyle(this.getEl(), attribute, val + unit);
   }
};

/**
 * @param {HTMLElement | String} el Reference to the element that will be animated
 * @param {Object} attributes The attribute(s) to be animated.
 * Each attribute is an object with at minimum a "to" or "by" member defined.
 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
 * All attribute names use camelCase.
 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
 */
YAHOO.util.Motion.prototype.initMotion = function(el, attributes, duration, method) {
   YAHOO.util.Anim.call(this, el, attributes, duration, method);

   attributes = attributes || {};
   attributes.points = attributes.points || {};
   attributes.points.control = attributes.points.control || [];

   this.attributes = attributes;

   var start;
   var end = null;
   var translatedPoints = null;

   this.getTranslatedPoints = function() { return translatedPoints; };

   var translateValues = function(val, self) {
      var pageXY = YAHOO.util.Dom.getXY(self.getEl());
      val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];

      return val;
   };

   var onStart = function() {
      start = this.getAttribute('points');
      var attributes = this.attributes;
      var control =  attributes['points']['control'] || [];

      if (control.length > 0 && control[0].constructor != Array) { // could be single point or array of points
         control = [control];
      }

      if (YAHOO.util.Dom.getStyle(this.getEl(), 'position') == 'static') { // default to relative
         YAHOO.util.Dom.setStyle(this.getEl(), 'position', 'relative');
      }

      if (typeof attributes['points']['from'] != 'undefined') {
         YAHOO.util.Dom.setXY(this.getEl(), attributes['points']['from']); // set to from point
         start = this.getAttribute('points'); // get actual offset values
      }
      else if ((start[0] === 0 || start[1] === 0)) { // these sometimes up when auto
         YAHOO.util.Dom.setXY(this.getEl(), YAHOO.util.Dom.getXY(this.getEl())); // set it to current position, giving offsets
         start = this.getAttribute('points'); // get actual offset values
      }

      var i, len;
      // TO beats BY, per SMIL 2.1 spec
      if (typeof attributes['points']['to'] != 'undefined') {
         end = translateValues(attributes['points']['to'], this);

         for (i = 0, len = control.length; i < len; ++i) {
            control[i] = translateValues(control[i], this);
         }

      } else if (typeof attributes['points']['by'] != 'undefined') {
         end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1]];

         for (i = 0, len = control.length; i < len; ++i) {
            control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
         }
      }

      if (end) {
         translatedPoints = [start];

         if (control.length > 0) { translatedPoints = translatedPoints.concat(control); }

         translatedPoints[translatedPoints.length] = end;
      }
   };

   this._onStart.subscribe(onStart);
};

/**
 * @class Anim subclass for scrolling elements to a position defined by the "scroll" member of "attributes".  All "scroll" members are arrays with x, y scroll positions.
 * <p>Usage: <code>var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
 * @requires YAHOO.util.Anim
 * @requires YAHOO.util.AnimMgr
 * @requires YAHOO.util.Easing
 * @requires YAHOO.util.Bezier
 * @requires YAHOO.util.Dom
 * @requires YAHOO.util.Event
 * @constructor
 * @param {HTMLElement | String} el Reference to the element that will be animated
 * @param {Object} attributes The attribute(s) to be animated.
 * Each attribute is an object with at minimum a "to" or "by" member defined.
 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
 * All attribute names use camelCase.
 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
 */
YAHOO.util.Scroll = function(el, attributes, duration,  method) {
   if (el) {
      YAHOO.util.Anim.call(this, el, attributes, duration, method);
   }
};

YAHOO.util.Scroll.prototype = new YAHOO.util.Anim();

/**
 * Per attribute units that should be used by default.
 * Scroll positions default to no units.
 * @type Object
 */
YAHOO.util.Scroll.prototype.defaultUnits.scroll = ' ';

/**
 * Returns the value computed by the animation's "method".
 * @param {String} attribute The name of the attribute.
 * @param {Number} start The value this attribute should start from for this animation.
 * @param {Number} end  The value this attribute should end at for this animation.
 * @return {Number} The Value to be applied to the attribute.
 */
YAHOO.util.Scroll.prototype.doMethod = function(attribute, start, end) {
   var val = null;

   if (attribute == 'scroll') {
      val = [
         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
      ];

   } else {
      val = this.method(this.currentFrame, start, end - start, this.totalFrames);
   }
   return val;
};

/**
 * Returns current value of the attribute.
 * @param {String} attribute The name of the attribute.
 * @return {Number} val The current value of the attribute.
 */
YAHOO.util.Scroll.prototype.getAttribute = function(attribute) {
   var val = null;
   var el = this.getEl();

   if (attribute == 'scroll') {
      val = [ el.scrollLeft, el.scrollTop ];
   } else {
      val = parseFloat( YAHOO.util.Dom.getStyle(el, attribute) );
   }

   return val;
};

/**
 * Applies a value to an attribute
 * @param {String} attribute The name of the attribute.
 * @param {Number} val The value to be applied to the attribute.
 * @param {String} unit The unit ('px', '%', etc.) of the value.
 */
YAHOO.util.Scroll.prototype.setAttribute = function(attribute, val, unit) {
   var el = this.getEl();

   if (attribute == 'scroll') {
      el.scrollLeft = val[0];
      el.scrollTop = val[1];
   } else {
      YAHOO.util.Dom.setStyle(el, attribute, val + unit);
   }
};
// BusyBoxDotNet. Web control library for ASP.NET to enhance user experience.
// Copyright (C) 2006  Simone Busoli, simone_b@tin.it
// Refer to Readme.txt and LGLP.txt for further details

// JavaScript Browser Sniffer 
// Eric Krok, Andy King, Michel Plungjan Jan. 31, 2002
// see http://www.webreference.com/ for more information
// Edited by Simone Busoli

BBDN.util.BrowserSniff = new function()
{
	var agt = navigator.userAgent.toLowerCase();
	var appVer = navigator.appVersion.toLowerCase();
	var iePos = appVer.indexOf('msie');

	this.is_opera = (agt.indexOf("opera") != -1);

	this.is_mac = (agt.indexOf("mac")!= -1);
	                                 
	this.is_konq = (agt.indexOf('konqueror') != -1);

	var is_getElementById   = (document.getElementById) ? "true" : "false"; // 001121-abk
	var is_getElementsByTagName = (document.getElementsByTagName) ? "true" : "false"; // 001127-abk
	var is_documentElement = (document.documentElement) ? "true" : "false"; // 001121-abk

	this.is_safari = ((agt.indexOf('safari')!=-1)&&(agt.indexOf('mac')!=-1))?true:false;
	this.is_khtml  = (this.is_safari || this.is_konq);

	this.is_gecko = ((!this.is_khtml)&&(navigator.product)&&(navigator.product.toLowerCase()=="gecko"))?true:false;

	this.is_fb = ((agt.indexOf('mozilla/5')!=-1) && (agt.indexOf('spoofer')==-1) &&
					(agt.indexOf('compatible')==-1) && (agt.indexOf('opera')==-1)  &&
					(agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1)     &&
					(this.is_gecko) && (navigator.vendor=="Firebird"));
	this.is_fx = ((agt.indexOf('mozilla/5')!=-1) && (agt.indexOf('spoofer')==-1) &&
					(agt.indexOf('compatible')==-1) && (agt.indexOf('opera')==-1)  &&
					(agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1)     &&
					(this.is_gecko) && ((navigator.vendor=="Firefox")||(agt.indexOf('firefox')!=-1)));
	this.is_moz   = ((agt.indexOf('mozilla/5')!=-1) && (agt.indexOf('spoofer')==-1) &&
					(agt.indexOf('compatible')==-1) && (agt.indexOf('opera')==-1)  &&
					(agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1)     &&
					(this.is_gecko) && (!this.is_fb) && (!this.is_fx) &&
					((navigator.vendor=="")||(navigator.vendor=="Mozilla")||(navigator.vendor=="Debian")));

	this.is_nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
				&& (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
				&& (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1)
				&& (!this.is_khtml) && (!(this.is_moz)) && (!this.is_fb) && (!this.is_fx));

	this.is_ie   = ((iePos!=-1) && (!this.is_opera) && (!this.is_khtml));
};
// BusyBoxDotNet. Web control library for ASP.NET to enhance user experience.
// Copyright (C) 2006  Simone Busoli, simone_b@tin.it
// Refer to Readme.txt and LGLP.txt for further details

BBDN.util.Dom = new function()
{
	this.verticalScrollBarVisible = function()
	{
		return (document.documentElement.clientHeight < document.documentElement.scrollHeight );
	};

	this.horizontalScrollBarVisible =  function()
	{
		return (document.documentElement.clientWidth < document.documentElement.scrollWidth );
	};

	this.bothScrollBarsVisible = function()
	{
		return (this.verticalScrollBarVisible() && this.horizontalScrollBarVisible());
	};

	// Gets the total width of the page, including horizontal scrolling
	this.getTotalWidth = function()
	{
		var width;
		
		// fix for firefox and netscape which shows scrollbars if one of the scrollbars is visible
		// DON'T CHANGE!!!
		if(BBDN.util.BrowserSniff.is_fx || BBDN.util.BrowserSniff.is_nav)
			if(this.verticalScrollBarVisible() && !this.bothScrollBarsVisible())
				return document.documentElement.clientWidth;
		
		this.getInnerWidth() > this.getScrollWidth() ?  
			width = this.getInnerWidth() : 
			width = this.getScrollWidth();
		
		return width;
	};

	// Gets the total height of the page, including vertical scrolling
	this.getTotalHeight = function()
	{
		var height;
		
		// fix for firefox and netscape which shows scrollbars if one of the scrollbars is visible
		// DON'T CHANGE!!!
		if(BBDN.util.BrowserSniff.is_fx || BBDN.util.BrowserSniff.is_nav)
			if(this.horizontalScrollBarVisible() && !this.bothScrollBarsVisible())
				return document.documentElement.clientHeight;
		
		this.getInnerHeight() > this.getScrollHeight() ? 
			height = this.getInnerHeight() : 
			height = this.getScrollHeight();
		
		return height;
	};

	// viewport width
	this.getInnerWidth = function()
	{
		var x;
		
		if (self.innerWidth) 
			x = self.innerWidth;
		else if (document.documentElement && document.documentElement.clientWidth)
			x = document.documentElement.clientWidth;
		else if (document.body) 
			x = document.body.clientWidth;
		
		return x;
	};

	// viewport height
	this.getInnerHeight = function()
	{
		var y;
		
		if (self.innerHeight) 
			y = self.innerHeight;
		else if (document.documentElement && document.documentElement.clientHeight)
			y = document.documentElement.clientHeight;
		else if (document.body) 
			y = document.body.clientHeight;
		
		return y;
	};

	this.getScrollOffsetWidth = function()
	{
		var x;
		
		if (self.pageYOffset)
			x = self.pageXOffset;
		else if (document.documentElement && document.documentElement.scrollTop)
			x = document.documentElement.scrollLeft;
		else if (document.body)
			x = document.body.scrollLeft;
		
		return x;
	};

	this.getScrollOffsetHeight = function()
	{
		var y;
		
		if (self.pageYOffset) 
			y = self.pageYOffset;
		else if (document.documentElement && document.documentElement.scrollTop)
			y = document.documentElement.scrollTop;
		else if (document.body) 
			y = document.body.scrollTop;
		
		return y;
	};

	this.getScrollWidth = function()
	{
		var x = 0;
		
		if (document.documentElement && document.documentElement.scrollWidth)
			x = document.documentElement.scrollWidth;
		else if (document.body)
			x = document.body.scrollWidth;
		
		return x;
	};

	this.getScrollHeight = function()
	{
		var y = 0;
		
		if (document.documentElement && document.documentElement.scrollHeight)
			y = document.documentElement.scrollHeight;
		else if (document.body)
			y = document.body.scrollHeight;
		
		return y;
	};

	// gets the actual width of the document body element
	// doesn't correspond to browser's window size
	this.getBodyWidth = function()
	{
		var x;
		
		var test1 = document.body.scrollWidth;
		var test2 = document.body.offsetWidth;

		if (test1 > test2) // all but Explorer Mac
			x = document.body.scrollWidth;
		else 
			x = document.body.offsetWidth;
		
		return x;
	};

	// gets the actual height of the document body element
	// doesn't correspond to browser's window size
	this.getBodyHeight = function()
	{
		var y;
		
		var test1 = document.body.scrollHeight;
		var test2 = document.body.offsetHeight;

		if (test1 > test2)
			y = document.body.scrollHeight;
		else 
			y = document.body.offsetHeight;
		
		return y;
	};

	this.hideSelects = function()
	{
		if (BBDN.util.BrowserSniff.is_ie)
		{
			var selects = [];
			selects = document.all.tags("SELECT");
			for	(var i = 0; i < selects.length; i++)
				selects[i].runtimeStyle.visibility = "hidden";
		}
	};

	this.showSelects = function()
	{
		if (BBDN.util.BrowserSniff.is_ie)
		{
			var selects = [];
			selects = document.all.tags("SELECT");
			for	(var i = 0; i < selects.length; i++)
				selects[i].runtimeStyle.visibility = "";   
		}
	};

	this.setOpacity = function(element, opacity)
	{
		element.style.filter = "alpha(opacity:" + opacity + ")"; 
		element.style.KHTMLOpacity = opacity / 100;
		element.style.MozOpacity = opacity / 100; 
		element.style.opacity = opacity / 100;
	};

	this.documentLoaded = function()
	{
		if(document.readyState != "complete") 
			return false;
		return true;
	};

	this.checkValidators = function()
	{
		try
		{
			if(Page_IsValid != null) 
				return Page_IsValid; 
			
			return true;
		}
		// no validators on page -> page is obviously valid
		catch(e)
		{ return true; }
	};
	
	this.$ = function()
	{
		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 (arguments.length == 1)
				return element;
			elements.push(element);
		}
		return elements;
	};
	
	this.getAbsoluteLeft = function(o) 
	{
		var oLeft = o.offsetLeft;        
		var oParent;   
		
		while(o.offsetParent !== null) 
		{   
			oParent = o.offsetParent;    
			oLeft += oParent.offsetLeft; 
			o = oParent;
		}
		return oLeft;
	};

	this.getAbsoluteTop = function(o) 
	{
		var oTop = o.offsetTop;   
		var oParent;         
		
		while(o.offsetParent!=null) 
		{ 
			oParent = o.offsetParent;  
			oTop += oParent.offsetTop; 
			o = oParent;
		}
		return oTop;
	};
	
	// getElementSize: return width and height
	this.getElementSize = function(obj)
	{
		var w = obj.offsetWidth;
		var h = obj.offsetHeight;
		return {width : w, height : h};
	};
}; // end BBDN.util.Dom

BBDN.util.Yahoo = new function()
{
	this.getEasingFromString = function(easing)
	{
		switch(easing)
		{
			case "easeout":		return YAHOO.util.Easing.easeOut; 
			case "easeboth":	return YAHOO.util.Easing.easeBoth;
			case "easein":		return YAHOO.util.Easing.easeIn;
			case "easenone":	return YAHOO.util.Easing.easeNone;
			case "backboth":	return YAHOO.util.Easing.backBoth;
			case "backin":		return YAHOO.util.Easing.backIn;  
			case "backout":		return YAHOO.util.Easing.backOut; 
			default:			return YAHOO.util.Easing.easeOut;
		}
	} // end getYahooEasingFromString
};
// #pragma NoCompStart
// BusyBoxDotNet. Web control library for ASP.NET to enhance user experience.
// Copyright (C) 2006  Simone Busoli, simone_b@tin.it
// #pragma NoCompEnd
// Refer to Readme.txt and LGLP.txt for further details

BBDN.core.BusyBox = function (	id, 
								title, 
								text,
								width, 
								height,
								imageUrl, 
								layout,
								overlay,
								immediatelyOverlay, 
								keepOverlay, 
								overlayColor, 
								overlayOpacity,
								opacity,
								borderWidth,
								borderColor,
								borderStyle,
								backColor,					
								textWeight,
								textStyle,
								textSize,
								textFamily,
								titleWeight,
								titleStyle,
								titleSize,
								titleFamily,
								textColor,
								titleColor,
								fadeInDuration,
								fadeInEasing,
								fadeOnMouseOver,
								fadeOnMouseOverOpacity,
								position,
								keepPosition,
								slideDuration,
								slideEasing,
								roundCorners,
								showOnLoad,
								anchorControlID,
								dockPosition
							)
{
	var self = this;

	this.ID = id;
	this.Title = title;
	this.Text = text;
	
	if(width !== "")
		this.Width = width;
	else
		this.Width = "300px";
		
	if(height !== "")
		this.Height = height;
	else
		this.Height = "60px";
		
	this.ImageUrl = imageUrl;
	this.Layout = layout;
	this.Overlay = overlay;
	this.ImmediatelyOverlay = immediatelyOverlay;
	this.KeepOverlay = keepOverlay;
	
	// if overlaycolor not specified  set the actual body background color
	// no transparent background allowed
    if(overlayColor === "")
    {
		if(document.body.currentStyle)
			this.OverlayColor = document.body.currentStyle.backgroundColor;
        
		if(document.defaultView)
			this.OverlayColor = document.defaultView.getComputedStyle(document.body, "").getPropertyValue("background-color");
    
		// if body background color is not specified make it white
		if(this.OverlayColor == "transparent")
			this.OverlayColor = "#fff";
    }    
    else
    {
		this.OverlayColor = overlayColor;
	}
	
	this.OverlayOpacity = overlayOpacity;
	this.Opacity = opacity;
	
	if(borderWidth !== "")
		this.BorderWidth = borderWidth;
	else
		this.BorderWidth = "1px";
	
	if(borderColor !== "")
		this.BorderColor = borderColor;
	else
		this.BorderColor = "black";

	if(borderStyle !== "NotSet")
		this.BorderStyle = borderStyle;
	else
		this.BorderStyle = "solid";
			
	if(backColor === "")
		this.BackColor = "#ffffff";
	else
		this.BackColor = backColor;
	
	this.TextWeight = textWeight;
	this.TextStyle = textStyle;
	this.TextSize =	textSize;
	this.TextFamily = textFamily;
	this.TitleWeight = titleWeight;
	this.TitleStyle = titleStyle;
	this.TitleSize = titleSize;
	this.TitleFamily = titleFamily;	
	
	if(textColor !== "")
		this.TextColor = textColor;
	else
		this.TextColor = "black";
	
	if(titleColor !== "")
		this.TitleColor = titleColor;
	else
		this.TitleColor = "black";
	
	this.FadeInDuration = fadeInDuration;
	
	this.FadeOnMouseOver = fadeOnMouseOver;
	this.FadeOnMouseOverOpacity = fadeOnMouseOverOpacity;
	
	this.FinishedFading = false;
	this.Visible = false;
	
	this.Position = position;
	this.KeepPosition = keepPosition;
	
	this.SlideDuration = slideDuration;
	
	this.SlideEasing = BBDN.util.Yahoo.getEasingFromString(slideEasing);
	this.FadeInEasing = BBDN.util.Yahoo.getEasingFromString(fadeInEasing);
	
	// not working yet
	this.RoundCorners = roundCorners;
	
	this.ShowOnLoad = showOnLoad;
	
	this.AnchorControlID = anchorControlID;
	this.DockPosition = dockPosition;
	
	//
	// Privileged members
	//
	
	this.Show = function()
	{	
		// if validators don't validate return and don't show the box
		if(!BBDN.util.Dom.checkValidators())
			return;
			
		// not null only if ShowOnLoad is true
		this.container = BBDN.util.Dom.$(this.ID + "Container");
		
		// create and show the box
		create();	
		
   		BBDN.util.Dom.hideSelects();       
		
		// subscribe events to keep the box positioned
		if(this.KeepPosition && this.Position !== "dock")
		{
			YAHOO.util.Event.addListener(window, "resize", slideToPosition, this, true);
			YAHOO.util.Event.addListener(window, "scroll", slideToPosition, this, true);
			YAHOO.util.Event.addListener(window, "DOMMouseScroll", slideToPosition, this, true);
		}
		
		if(this.Overlay && !this.ImmediatelyOverlay && !this.ShowOnLoad)
		{
			this.CreateOverlay(); 
		}
			
		if(this.FadeOnMouseOver)
		{		
			YAHOO.util.Event.addListener(this.box, "mouseover", makeTransparent, this, true);
			YAHOO.util.Event.addListener(this.box, "mouseout", resetTransparency, this, true);
		}
	}; // end Show
	
	this.Close = function()
	{	
		if(this.Overlay && !this.KeepOverlay && !this.ShowOnLoad)
		{
			// Remove Overlay
			var overlay = BBDN.util.Dom.$(this.ID + "Overlay");
			
			if(overlay !== null)
				document.body.removeChild(overlay);
		}
		
		this.Visible = false;
	    		
		if(this.box !== null)
		{	    			
			if(this.ShowOnLoad)
			{
				this.container.removeChild(this.box);
				window.clearInterval(this.hideSelectsTimer);
				BBDN.util.Dom.showSelects(); 
			}
			else
				document.body.removeChild(this.box);
		}
		
		BBDN.util.Dom.showSelects();
	}; // end Close
	
	this.CreateOverlay = function()
	{
		var d = document;	
		var overlay = d.createElement("div");
	     		
		document.body.appendChild(overlay);
	    
		overlay.id = this.ID + "Overlay";
	    
		overlay.style.position = "absolute";  
		//overlay.style.display = "block";  
			
		overlay.style.top = "0px";
		overlay.style.left = "0px";
				
		overlay.style.zIndex = "9998";
	    
		overlay.style.width = BBDN.util.Dom.getTotalWidth() + "px";
		overlay.style.height = BBDN.util.Dom.getTotalHeight() + "px";
		  
		if(BBDN.util.BrowserSniff.is_opera)
		{
			// adjust this
			overlay.style.backgroundImage = "url(BusyBoxDotNet.axd?res=maskBG.png)"; 
			overlay.style.backgroundRepeat = "repeat";
		}
		else
		{
			overlay.style.backgroundColor = this.OverlayColor;   
			BBDN.util.Dom.setOpacity(overlay, this.OverlayOpacity);  
		}
	}; // end CreateOverlay
	
	//
	// Private members
	//
	
	function create()
	{    
		// should never happen to find an instance of the box
		if(BBDN.util.Dom.$(self.ID))
			self.Close();
	    
		// create the div element
		self.box = document.createElement("div");
		
		if(self.ShowOnLoad)
		{
			// append the box to the container
			self.container.appendChild(self.box);
			// needed to hide selects eventually rendered during page load at intervals
			self.hideSelectsTimer = window.setInterval(BBDN.util.Dom.hideSelects, 100); 
		}
		else
			// append the box to the body
			document.body.appendChild(self.box);		
		
		// set element attributes
		self.box.style.display = "none"; 
		self.box.id = self.ID; 
		self.box.style.position = "absolute";
		self.box.style.zIndex = "9999";
				
		self.box.style.borderWidth = self.BorderWidth;
		self.box.style.borderColor = self.BorderColor;		
		self.box.style.borderStyle = self.BorderStyle;
		self.box.style.backgroundColor = self.BackColor;
		
		// set the chosen template
		setTemplate();		
		
		self.box.style.display = "block"; 
				
		self.Visible = true;
		
		/*if(self.RoundCorners)
			Rico.Corner.round(self.ID);*/
		
		// set position for the first time (then will subscribe scroll and resize events)
		setPosition();
		
		// fade in if the case or just set the final opacity
		fadeIn();	
	} // end create
	
	function setPosition()
	{
		if(self.Visible)
		{			
			var fullHeight = BBDN.util.Dom.getInnerHeight();
			var fullWidth = BBDN.util.Dom.getInnerWidth();
			
			var scTop = document.documentElement.scrollTop ? 
				document.documentElement.scrollTop : document.body.scrollTop;
				
			var scrollTop = parseInt(scTop,10);
			
			var scLeft = document.documentElement.scrollLeft ? 
				document.documentElement.scrollLeft : document.body.scrollLeft;
				
			var scrollLeft = parseInt(scLeft,10);
				
			/*	original (the new above method should work even if the document is not xhtml)
			IT DOESNT WORK EITHER IF DOCTYPE IS NOT XHTML
				
			var scrollTop = parseInt(document.documentElement.scrollTop,10);
			var scrollLeft = parseInt(document.documentElement.scrollLeft,10);*/
			
			if(self.ShowOnLoad)
			{
				scrollTop = scrollTop - BBDN.util.Dom.getAbsoluteTop(self.container.offsetParent);
				scrollLeft = scrollLeft - BBDN.util.Dom.getAbsoluteLeft(self.container.offsetParent);
			}
			
			switch(self.Position)
			{
				case "center": 
				
					self.box.style.top = (scrollTop  + ((fullHeight - self.box.offsetHeight) / 2)) + "px";
					self.box.style.left =  (scrollLeft + ((fullWidth - self.box.offsetWidth) / 2)) + "px";
					break;
					
				case "righttop":
				
					self.box.style.top = (scrollTop + 10) + "px";
					self.box.style.left =  (scrollLeft + fullWidth - self.box.offsetWidth - 20) + "px";
					break;
					
				case "lefttop":
					
					self.box.style.top = (scrollTop + 10) + "px";
					self.box.style.left =  (scrollLeft + 10) + "px";
					break;
					
				case "leftbottom":
				
					self.box.style.top = (scrollTop + fullHeight - self.box.offsetHeight - 20) + "px";
					self.box.style.left = (scrollLeft + 10) + "px";
					break;

				case "rightbottom":
				
					self.box.style.top = (scrollTop + fullHeight - self.box.offsetHeight - 20) + "px";
					self.box.style.left =  (scrollLeft + fullWidth - self.box.offsetWidth - 20) + "px";
					break;
					
				case "dock":
				
					var size = BBDN.util.Dom.getElementSize(self.box);					
					var windowsize = BBDN.util.Dom.getElementSize(document.body);
					var parentelementposition = YAHOO.util.Dom.getXY(BBDN.util.Dom.$(self.AnchorControlID));
					var parentelementsize = BBDN.util.Dom.getElementSize(BBDN.util.Dom.$(self.AnchorControlID));
					
					if(self.DockPosition === "auto")	// find best position
					{
						if((windowsize.width - parentelementposition[0] - parentelementsize.width - size.width) > 0 && (parentelementposition[1] + parentelementsize.height / 2 - size.height / 2) > 0)
							self.DockPosition = "right";
						else if((parentelementposition[0] - size.width) > 0 && (parentelementposition[1] + parentelementsize.height / 2 - size.height / 2) > 0)
							self.DockPosition = "left";
						else if((parentelementposition[0] + parentelementsize.width / 2 - size.width / 2) > 0 && (parentelementposition[1] - size.height) > 0)
							self.DockPosition = "top";
						else
							self.DockPosition = "bottom";
					}
					
					switch(self.DockPosition)
					{
						case "right":
							self.box.style.left = parentelementposition[0] + parentelementsize.width + "px";
							self.box.style.top = parentelementposition[1] + parentelementsize.height / 2 - size.height / 2 + "px";
							
							break;
							
						case "left":
							self.box.style.left = parentelementposition[0] - size.width + "px";
							self.box.style.top = parentelementposition[1] + parentelementsize.height / 2 - size.height / 2 + "px";
							
							break;
							
						case "top":
							self.box.style.left = parentelementposition[0] + parentelementsize.width / 2 - size.width / 2 + "px";
							self.box.style.top = parentelementposition[1] - size.height + "px";
							
							break;
							
						case "bottom":
							self.box.style.left = parentelementposition[0] + parentelementsize.width / 2 - size.width / 2 + "px";
							self.box.style.top = parentelementposition[1] + parentelementsize.height + "px";
						
							break;
					} // end switch	
					
					break;
			} // end switch
		} // end if
	} // end setPosition

	function slideToPosition()
	{
		if(self.Visible)
		{	
			var fullHeight = BBDN.util.Dom.getInnerHeight();
			var fullWidth = BBDN.util.Dom.getInnerWidth();
			
			var scTop = document.documentElement.scrollTop ? 
				document.documentElement.scrollTop : document.body.scrollTop;
				
			var scrollTop = parseInt(scTop,10);
			
			var scLeft = document.documentElement.scrollLeft ? 
				document.documentElement.scrollLeft : document.body.scrollLeft;
				
			var scrollLeft = parseInt(scLeft,10);
			
			//if ShowOnLoad the box is in the container and to center it we need to calculate its absolute position
			if(self.ShowOnLoad)
			{
				scrollTop = scrollTop - BBDN.util.Dom.getAbsoluteTop(self.container.offsetParent);
				scrollLeft = scrollLeft - BBDN.util.Dom.getAbsoluteLeft(self.container.offsetParent);
			}
			
			var xPos, yPos;
						
			switch(self.Position)
			{
				case "center":		xPos = scrollLeft + ((fullWidth - self.box.offsetWidth) / 2);
									yPos = scrollTop + ((fullHeight - self.box.offsetHeight) / 2);
									break;
								
				case "righttop":	yPos = scrollTop + 10;
									xPos =  scrollLeft + fullWidth - self.box.offsetWidth - 20;
									break;	
				
				case "lefttop":		yPos = scrollTop + 10;
									xPos =  scrollLeft + 10;
									break;
									
				case "leftbottom": 	yPos = scrollTop + fullHeight - self.box.offsetHeight - 20;
									xPos = scrollLeft + 10;
									break;
									
				case "rightbottom": yPos = scrollTop + fullHeight - self.box.offsetHeight - 20;
									xPos = scrollLeft + fullWidth - self.box.offsetWidth - 20;
									break;				
			}
			
			var attributes = {
					top: { to: yPos },
					left: { to: xPos }
				};
								
			var anim = new YAHOO.util.Anim(self.ID, attributes, (self.SlideDuration / 1000), self.SlideEasing);
				
			anim.animate();
		}
	} // end slideToPosition

	function fadeIn()
	{
		if(self.Visible)
		{
			// actually fade in
			if(self.FadeInDuration > 0)
			{
				BBDN.util.Dom.setOpacity(self.box, 0);
				
				var attributes = { 
					opacity: { to: (self.Opacity / 100) } 
				};
				
				var anim = new YAHOO.util.Anim(self.ID, attributes, (self.FadeInDuration / 1000), self.FadeInEasing);
								
				anim.onComplete.subscribe(onFinishedFading, self, true);
				
				anim.animate();
			}
			// just show without fading
			else
			{
				BBDN.util.Dom.setOpacity(self.box, self.Opacity);
				self.FinishedFading = true;
			}
		}
	} // end fadeIn
	
	function onFinishedFading()
	{
		self.FinishedFading = true;
	}
	
	function makeTransparent()
	{		
		if(self.FinishedFading)
			BBDN.util.Dom.setOpacity(self.box, self.FadeOnMouseOverOpacity);
	}
	
	function resetTransparency()
	{
		if(self.FinishedFading)
			BBDN.util.Dom.setOpacity(self.box, self.Opacity);
	}
	
	function setTemplate()
	{
		var tableID = self.ID + "Table";
		var textID = self.ID + "Text";
		var titleID = self.ID + "Title";
		var imageID = self.ID + "Image";

		switch(self.Layout)
		{
			case "classic": 
			
					self.box.innerHTML = 
					"<table id=\"" + tableID + "\" border=0 cellpadding=0 cellspacing=0>" + 
					"	<tr valign=\"top\">" +
					"		<td rowspan=\"2\"><img border=\"0\" id=\"" + imageID + "\" src=\"" + self.ImageUrl + "\" /></td>" +
					"		<td width=\"100%\"><p id=\"" + titleID + "\">" + self.Title + "</p></td>" +
					"	</tr>" +
					"	<tr valign=\"top\">" +
					"		<td><p id=\"" + textID + "\">" + self.Text + "</p></td>" +
					"	</tr>" +
					"</table>"; 
					
					var table = BBDN.util.Dom.$(tableID);
		    
					table.style.width = self.Width;
					table.style.height = self.Height;
				    
					var title = BBDN.util.Dom.$(titleID);
				    
					title.style.fontWeight = self.TitleWeight;
					title.style.fontStyle = self.TitleStyle;
					title.style.fontSize = self.TitleSize;
					title.style.fontFamily = self.TitleFamily;
					title.style.margin = "15px 10px 0px 10px";
				    
					title.style.color = self.TitleColor;
				    
					var text = BBDN.util.Dom.$(textID);
				    
					text.style.fontWeight = self.TextWeight;
					text.style.fontStyle = self.TextStyle;
					text.style.fontSize = self.TextSize;
					text.style.fontFamily = self.TextFamily;
					text.style.margin = "20px 10px 15px 10px";
				    
					text.style.color = self.TextColor;
						
					var image = BBDN.util.Dom.$(imageID);
					
					image.style.margin = "15px 5px 10px 10px";
					
					break;
			
			case "classicbottomimage":
					
					self.box.innerHTML = 
					"<table id=\"" + tableID + "\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">" + 
					"	<tr valign=\"top\">" +
					"		<td width=\"100%\"><p id=\"" + titleID + "\">" + self.Title + "</p></td>" +
					"	</tr>" +
					"	<tr valign=\"top\">" +
					"		<td><p id=\"" + textID + "\">" + self.Text + "</p></td>" +
					"	</tr>" +
					"	<tr valign=\"top\">" +
					"		<td align=\"center\"><img border=\"0\" id=\"" + imageID + "\" src=\"" + self.ImageUrl + "\" /></td>" +
					"	</tr>" +
					"</table>";
		            
					var table = BBDN.util.Dom.$(tableID);
				    
					table.style.width = self.Width;
					table.style.height = self.Height;
				    
					var title = BBDN.util.Dom.$(titleID);
				    
					title.style.fontWeight = self.TitleWeight;
					title.style.fontStyle = self.TitleStyle;
					title.style.fontSize = self.TitleSize;
					title.style.fontFamily = self.TitleFamily;
					title.style.margin = "15px 10px 0px 10px";
	   				title.style.color = self.TitleColor;
				    
					var text = BBDN.util.Dom.$(textID);
				    
					text.style.fontWeight = self.TextWeight;
					text.style.fontStyle = self.TextStyle;
					text.style.fontSize = self.TextSize;
					text.style.fontFamily = self.TextFamily;
					text.style.margin = "20px 10px 15px 10px";
					text.style.color = self.TextColor;
						
					var image = BBDN.util.Dom.$(imageID);
					
					image.style.margin = "0px 0px 10px 0px";
				
					break;
					
			case "imageonly":
					
					self.box.innerHTML = "<img border=\"0\" id=\"" + imageID + "\" src=\"" + self.ImageUrl + "\" />";
		            
					var image = BBDN.util.Dom.$(imageID);
					
					image.style.margin = "0px 0px 0px 0px";
				
					break;
					
			case "textonly":
			
					self.box.innerHTML = 
					
					"<span id=\"" + textID + "\">" + self.Text + "</span>";
					
					var text = BBDN.util.Dom.$(textID);
				    
					text.style.fontWeight = self.TextWeight;
					text.style.fontStyle = self.TextStyle;
					text.style.fontSize = self.TextSize;
					text.style.fontFamily = self.TextFamily;
					text.style.margin = "20px 10px 15px 10px";
				    
					text.style.color = self.TextColor;
						
					break;
		} // end switch
	} // end setTemplate
} // end BBDN.core.BusyBox
