text: /*
  Copyright (c) 2011, Yahoo! Inc. All rights reserved.
  Code licensed under the BSD License:
  http://developer.yahoo.com/yui/license.html
  version: 2.9.0
  */
  
  /**
   * 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}  context 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.
   * @param {boolean} silent pass true to prevent the event from writing to
   *                  the debugsystem
   * @param {int}     signature the signature that the custom event subscriber
   *                  will receive. YAHOO.util.CustomEvent.LIST or
   *                  YAHOO.util.CustomEvent.FLAT.  The default is
   *                  YAHOO.util.CustomEvent.LIST.
   * @param fireOnce {boolean} If configured to fire once, the custom event
   * will only notify subscribers a single time regardless of how many times
   * the event is fired.  In addition, new subscribers will be notified
   * immediately if the event has already been fired.
   * @namespace YAHOO.util
   * @class CustomEvent
   * @constructor
   */
  YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) {
  
      /**
       * The type of event, returned to subscribers when the event fires
       * @property type
       * @type string
       */
      this.type = type;
  
      /**
       * The context the event will fire from by default. Defaults to the window obj.
       * @property scope
       * @type object
       */
      this.scope = context || window;
  
      /**
       * By default all custom events are logged in the debug build. Set silent to true
       * to disable debug output for this event.
       * @property silent
       * @type boolean
       */
      this.silent = silent;
  
      /**
       * If configured to fire once, the custom event will only notify subscribers
       * a single time regardless of how many times the event is fired.  In addition,
       * new subscribers will be notified immediately if the event has already been
       * fired.
       * @property fireOnce
       * @type boolean
       * @default false
       */
      this.fireOnce = fireOnce;
  
      /**
       * Indicates whether or not this event has ever been fired.
       * @property fired
       * @type boolean
       * @default false
       */
      this.fired = false;
  
      /**
       * For fireOnce events the arguments the event was fired with are stored
       * so that new subscribers get the proper payload.
       * @property firedWith
       * @type Array
       */
      this.firedWith = null;
  
      /**
       * Custom events support two styles of arguments provided to the event
       * subscribers.
       * <ul>
       * <li>YAHOO.util.CustomEvent.LIST:
       *   <ul>
       *   <li>param1: event name</li>
       *   <li>param2: array of arguments sent to fire</li>
       *   <li>param3: <optional> a custom object supplied by the subscriber</li>
       *   </ul>
       * </li>
       * <li>YAHOO.util.CustomEvent.FLAT
       *   <ul>
       *   <li>param1: the first argument passed to fire.  If you need to
       *           pass multiple parameters, use and array or object literal</li>
       *   <li>param2: <optional> a custom object supplied by the subscriber</li>
       *   </ul>
       * </li>
       * </ul>
       *   @property signature
       *   @type int
       */
      this.signature = signature || YAHOO.util.CustomEvent.LIST;
  
      /**
       * The subscribers to this event
       * @property subscribers
       * @type Subscriber[]
       */
      this.subscribers = [];
  
      if (!this.silent) {
          YAHOO.log( "Creating " + this, "info", "Event" );
      }
  
      var onsubscribeType = "_YUICEOnSubscribe";
  
      // Only add subscribe events for events that are not generated by
      // CustomEvent
      if (type !== onsubscribeType) {
  
          /**
           * Custom events provide a custom event that fires whenever there is
           * a new subscriber to the event.  This provides an opportunity to
           * handle the case where there is a non-repeating event that has
           * already fired has a new subscriber.
           *
           * @event subscribeEvent
           * @type YAHOO.util.CustomEvent
           * @param fn {Function} The function to execute
           * @param obj <Object> An object to be passed along when the event fires.
           * Defaults to the custom event.
           * @param override <boolean|Object> If true, the obj passed in becomes the
           * execution context of the listener. If an object, that object becomes
           * the execution context. Defaults to the custom event.
           */
          this.subscribeEvent =
                  new YAHOO.util.CustomEvent(onsubscribeType, this, true);
  
      }
  
  
      /**
       * In order to make it possible to execute the rest of the subscriber
       * stack when one thows an exception, the subscribers exceptions are
       * caught.  The most recent exception is stored in this property
       * @property lastError
       * @type Error
       */
      this.lastError = null;
  };
  
  /**
   * Subscriber listener sigature constant.  The LIST type returns three
   * parameters: the event type, the array of args passed to fire, and
   * the optional custom object
   * @property YAHOO.util.CustomEvent.LIST
   * @static
   * @type int
   */
  YAHOO.util.CustomEvent.LIST = 0;
  
  /**
   * Subscriber listener sigature constant.  The FLAT type returns two
   * parameters: the first argument passed to fire and the optional
   * custom object
   * @property YAHOO.util.CustomEvent.FLAT
   * @static
   * @type int
   */
  YAHOO.util.CustomEvent.FLAT = 1;
  
  YAHOO.util.CustomEvent.prototype = {
  
      /**
       * Subscribes the caller to this event
       * @method subscribe
       * @param {Function} fn        The function to execute
       * @param {Object}   obj       An object to be passed along when the event
       * fires.
       * @param {boolean|Object} overrideContext If true, the obj passed in
       * becomes the execution.
       * context of the listener. If an object, that object becomes the execution
       * context.
       */
      subscribe: function(fn, obj, overrideContext) {
  
          if (!fn) {
  throw new Error("Invalid callback for subscriber to '" + this.type + "'");
          }
  
          if (this.subscribeEvent) {
              this.subscribeEvent.fire(fn, obj, overrideContext);
          }
  
          var s = new YAHOO.util.Subscriber(fn, obj, overrideContext);
  
          if (this.fireOnce && this.fired) {
              this.notify(s, this.firedWith);
          } else {
              this.subscribers.push(s);
          }
      },
  
      /**
       * Unsubscribes subscribers.
       * @method unsubscribe
       * @param {Function} fn  The subscribed function to remove, if not supplied
       *                       all will be removed
       * @param {Object}   obj  The custom object passed to subscribe.  This is
       *                        optional, but if supplied will be used to
       *                        disambiguate multiple listeners that are the same
       *                        (e.g., you subscribe many object using a function
       *                        that lives on the prototype)
       * @return {boolean} True if the subscriber was found and detached.
       */
      unsubscribe: function(fn, obj) {
  
          if (!fn) {
              return this.unsubscribeAll();
          }
  
          var found = false;
          for (var i=0, len=this.subscribers.length; i<len; ++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 context specified when the event was created, and with the
       * following parameters:
       *   <ul>
       *   <li>The type of event</li>
       *   <li>All of the arguments fire() was executed with as an array</li>
       *   <li>The custom object (if any) that was passed into the subscribe()
       *       method</li>
       *   </ul>
       * @method fire
       * @param {Object*} arguments an arbitrary set of parameters to pass to
       *                            the handler.
       * @return {boolean} false if one of the subscribers returned false,
       *                   true otherwise
       */
      fire: function() {
  
          this.lastError = null;
  
          var errors = [],
              len=this.subscribers.length;
  
  
          var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false;
  
          if (this.fireOnce) {
              if (this.fired) {
                  YAHOO.log('fireOnce event has already fired: ' + this.type);
                  return true;
              } else {
                  this.firedWith = args;
              }
          }
  
          this.fired = true;
  
          if (!len && this.silent) {
              //YAHOO.log('DEBUG no subscribers');
              return true;
          }
  
          if (!this.silent) {
              YAHOO.log( "Firing "       + this  + ", " +
                         "args: "        + args  + ", " +
                         "subscribers: " + len,
                         "info", "Event"                  );
          }
  
          // make a copy of the subscribers so that there are
          // no index problems if one subscriber removes another.
          var subs = this.subscribers.slice();
  
          for (i=0; i<len; ++i) {
              var s = subs[i];
              if (!s || !s.fn) {
                  rebuild=true;
              } else {
  
                  ret = this.notify(s, args);
  
                  if (false === ret) {
                      if (!this.silent) {
                          YAHOO.log("Event stopped, sub " + i + " of " + len, "info", "Event");
                      }
  
                      break;
                  }
              }
          }
  
          return (ret !== false);
      },
  
      notify: function(s, args) {
  
          var ret, param=null, scope = s.getScope(this.scope),
                   throwErrors = YAHOO.util.Event.throwErrors;
  
          if (!this.silent) {
              YAHOO.log( this.type + "-> " + s, "info", "Event" );
          }
  
          if (this.signature == YAHOO.util.CustomEvent.FLAT) {
  
              if (args.length > 0) {
                  param = args[0];
              }
  
              try {
                  ret = s.fn.call(scope, param, s.obj);
              } catch(e) {
                  this.lastError = e;
                  // errors.push(e);
                  YAHOO.log(this + " subscriber exception: " + e, "error", "Event");
                  if (throwErrors) {
                      throw e;
                  }
              }
          } else {
              try {
                  ret = s.fn.call(scope, this.type, args, s.obj);
              } catch(ex) {
                  this.lastError = ex;
                  YAHOO.log(this + " subscriber exception: " + ex, "error", "Event");
                  if (throwErrors) {
                      throw ex;
                  }
              }
          }
  
          return ret;
      },
  
      /**
       * Removes all listeners
       * @method unsubscribeAll
       * @return {int} The number of listeners unsubscribed
       */
      unsubscribeAll: function() {
          var l = this.subscribers.length, i;
          for (i=l-1; i>-1; i--) {
              this._delete(i);
          }
  
          this.subscribers=[];
  
          return l;
      },
  
      /**
       * @method _delete
       * @private
       */
      _delete: function(index) {
          var s = this.subscribers[index];
          if (s) {
              delete s.fn;
              delete s.obj;
          }
  
          // this.subscribers[index]=null;
          this.subscribers.splice(index, 1);
      },
  
      /**
       * @method toString
       */
      toString: function() {
           return "CustomEvent: " + "'" + this.type  + "', " +
               "context: " + this.scope;
  
      }
  };
  
  /////////////////////////////////////////////////////////////////////
  
  /**
   * Stores the subscriber information to be used when the event fires.
   * @param {Function} fn       The function to execute
   * @param {Object}   obj      An object to be passed along when the event fires
   * @param {boolean}  overrideContext If true, the obj passed in becomes the execution
   *                            context of the listener
   * @class Subscriber
   * @constructor
   */
  YAHOO.util.Subscriber = function(fn, obj, overrideContext) {
  
      /**
       * The callback that will be execute when the event fires
       * @property fn
       * @type function
       */
      this.fn = fn;
  
      /**
       * An optional custom object that will passed to the callback when
       * the event fires
       * @property obj
       * @type object
       */
      this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
  
      /**
       * The default execution context for the event listener is defined when the
       * event is created (usually the object which contains the event).
       * By setting overrideContext to true, the execution context becomes the custom
       * object passed in by the subscriber.  If overrideContext is an object, that
       * object becomes the context.
       * @property overrideContext
       * @type boolean|object
       */
      this.overrideContext = overrideContext;
  
  };
  
  /**
   * Returns the execution context for this listener.  If overrideContext was set to true
   * the custom obj will be the context.  If overrideContext is an object, that is the
   * context, otherwise the default context will be used.
   * @method getScope
   * @param {Object} defaultScope the context to use if this listener does not
   *                              override it.
   */
  YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
      if (this.overrideContext) {
          if (this.overrideContext === true) {
              return this.obj;
          } else {
              return this.overrideContext;
          }
      }
      return defaultScope;
  };
  
  /**
   * Returns true if the fn and obj match this objects properties.
   * Used by the unsubscribe method to match the right subscriber.
   *
   * @method contains
   * @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) {
      if (obj) {
          return (this.fn == fn && this.obj == obj);
      } else {
          return (this.fn == fn);
      }
  };
  
  /**
   * @method toString
   */
  YAHOO.util.Subscriber.prototype.toString = function() {
      return "Subscriber { obj: " + this.obj  +
             ", overrideContext: " +  (this.overrideContext || "no") + " }";
  };
  
  /**
   * The Event Utility provides utilities for managing DOM Events and tools
   * for building event systems
   *
   * @module event
   * @title Event Utility
   * @namespace YAHOO.util
   * @requires yahoo
   */
  
  // The first instance of Event will win if it is loaded more than once.
  // @TODO this needs to be changed so that only the state data that needs to
  // be preserved is kept, while methods are overwritten/added as needed.
  // This means that the module pattern can't be used.
  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 Event
   * @static
   */
      YAHOO.util.Event = function() {
  
          /**
           * True after the onload event has fired
           * @property loadComplete
           * @type boolean
           * @static
           * @private
           */
          var loadComplete =  false,
  
          /**
           * Cache of wrapped listeners
           * @property listeners
           * @type array
           * @static
           * @private
           */
          listeners = [],
  
  
          /**
           * User-defined unload function that will be fired before all events
           * are detached
           * @property unloadListeners
           * @type array
           * @static
           * @private
           */
          unloadListeners = [],
  
          /**
           * The number of times to poll after window.onload.  This number is
           * increased if additional late-bound handlers are requested after
           * the page load.
           * @property retryCount
           * @static
           * @private
           */
          retryCount = 0,
  
          /**
           * onAvailable listeners
           * @property onAvailStack
           * @static
           * @private
           */
          onAvailStack = [],
  
          /**
           * Counter for auto id generation
           * @property counter
           * @static
           * @private
           */
          counter = 0,
  
          /**
           * Normalized keycodes for webkit/safari
           * @property webkitKeymap
           * @type {int: int}
           * @private
           * @static
           * @final
           */
           webkitKeymap = {
              63232: 38, // up
              63233: 40, // down
              63234: 37, // left
              63235: 39, // right
              63276: 33, // page up
              63277: 34, // page down
              25: 9      // SHIFT-TAB (Safari provides a different key code in
                         // this case, even though the shiftKey modifier is set)
          },
  
          isIE = YAHOO.env.ua.ie,
  
          // String constants used by the addFocusListener and removeFocusListener methods
  
          FOCUSIN = "focusin",
          FOCUSOUT = "focusout";
  
          return {
  
              /**
               * The number of times we should look for elements that are not
               * in the DOM at the time the event is requested after the document
               * has been loaded.  The default is 500@amp;40 ms, so it will poll
               * for 20 seconds or until all outstanding handlers are bound
               * (whichever comes first).
               * @property POLL_RETRYS
               * @type int
               * @static
               * @final
               */
              POLL_RETRYS: 500,
  
              /**
               * The poll interval in milliseconds
               * @property POLL_INTERVAL
               * @type int
               * @static
               * @final
               */
              POLL_INTERVAL: 40,
  
              /**
               * Element to bind, int constant
               * @property EL
               * @type int
               * @static
               * @final
               */
              EL: 0,
  
              /**
               * Type of event, int constant
               * @property TYPE
               * @type int
               * @static
               * @final
               */
              TYPE: 1,
  
              /**
               * Function to execute, int constant
               * @property FN
               * @type int
               * @static
               * @final
               */
              FN: 2,
  
              /**
               * Function wrapped for context correction and cleanup, int constant
               * @property WFN
               * @type int
               * @static
               * @final
               */
              WFN: 3,
  
              /**
               * Object passed in by the user that will be returned as a
               * parameter to the callback, int constant.  Specific to
               * unload listeners
               * @property OBJ
               * @type int
               * @static
               * @final
               */
              UNLOAD_OBJ: 3,
  
              /**
               * Adjusted context, either the element we are registering the event
               * on or the custom object passed in by the listener, int constant
               * @property ADJ_SCOPE
               * @type int
               * @static
               * @final
               */
              ADJ_SCOPE: 4,
  
              /**
               * The original obj passed into addListener
               * @property OBJ
               * @type int
               * @static
               * @final
               */
              OBJ: 5,
  
              /**
               * The original context parameter passed into addListener
               * @property OVERRIDE
               * @type int
               * @static
               * @final
               */
              OVERRIDE: 6,
  
              /**
               * The original capture parameter passed into addListener
               * @property CAPTURE
               * @type int
               * @static
               * @final
               */
              CAPTURE: 7,
  
              /**
               * addListener/removeListener can throw errors in unexpected scenarios.
               * These errors are suppressed, the method returns false, and this property
               * is set
               * @property lastError
               * @static
               * @type Error
               */
              lastError: null,
  
              /**
               * Safari detection
               * @property isSafari
               * @private
               * @static
               * @deprecated use YAHOO.env.ua.webkit
               */
              isSafari: YAHOO.env.ua.webkit,
  
              /**
               * webkit version
               * @property webkit
               * @type string
               * @private
               * @static
               * @deprecated use YAHOO.env.ua.webkit
               */
              webkit: YAHOO.env.ua.webkit,
  
              /**
               * IE detection
               * @property isIE
               * @private
               * @static
               * @deprecated use YAHOO.env.ua.ie
               */
              isIE: isIE,
  
              /**
               * poll handle
               * @property _interval
               * @static
               * @private
               */
              _interval: null,
  
              /**
               * document readystate poll handle
               * @property _dri
               * @static
               * @private
               */
               _dri: null,
  
  
              /**
               * Map of special event types
               * @property _specialTypes
               * @static
               * @private
               */
              _specialTypes: {
                  focusin: (isIE ? "focusin" : "focus"),
                  focusout: (isIE ? "focusout" : "blur")
              },
  
  
              /**
               * True when the document is initially usable
               * @property DOMReady
               * @type boolean
               * @static
               */
              DOMReady: false,
  
              /**
               * Errors thrown by subscribers of custom events are caught
               * and the error message is written to the debug console.  If
               * this property is set to true, it will also re-throw the
               * error.
               * @property throwErrors
               * @type boolean
               * @default false
               */
              throwErrors: false,
  
  
              /**
               * @method startInterval
               * @static
               * @private
               */
              startInterval: function() {
                  if (!this._interval) {
                      // var self = this;
                      // var callback = function() { self._tryPreloadAttach(); };
                      // this._interval = setInterval(callback, this.POLL_INTERVAL);
                      this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true);
                  }
              },
  
              /**
               * Executes the supplied callback when the item with the supplied
               * id is found.  This is meant to be used to execute behavior as
               * soon as possible as the page loads.  If you use this after the
               * initial page load it will poll for a fixed time for the element.
               * The number of times it will poll and the frequency are
               * configurable.  By default it will poll for 10 seconds.
               *
               * <p>The callback is executed with a single parameter:
               * the custom object parameter, if provided.</p>
               *
               * @method onAvailable
               *
               * @param {string||string[]}   id the id of the element, or an array
               * of ids to look for.
               * @param {function} fn what to execute when the element is found.
               * @param {object}   obj an optional object to be passed back as
               *                   a parameter to fn.
               * @param {boolean|object}  overrideContext If set to true, fn will execute
               *                   in the context of obj, if set to an object it
               *                   will execute in the context of that object
               * @param checkContent {boolean} check child node readiness (onContentReady)
               * @static
               */
              onAvailable: function(id, fn, obj, overrideContext, checkContent) {
  
                  var a = (YAHOO.lang.isString(id)) ? [id] : id;
  
                  for (var i=0; i<a.length; i=i+1) {
                      onAvailStack.push({id:         a[i],
                                         fn:         fn,
                                         obj:        obj,
                                         overrideContext:   overrideContext,
                                         checkReady: checkContent });
                  }
  
                  retryCount = this.POLL_RETRYS;
  
                  this.startInterval();
              },
  
              /**
               * Works the same way as onAvailable, but additionally checks the
               * state of sibling elements to determine if the content of the
               * available element is safe to modify.
               *
               * <p>The callback is executed with a single parameter:
               * the custom object parameter, if provided.</p>
               *
               * @method onContentReady
               *
               * @param {string}   id the id of the element to look for.
               * @param {function} fn what to execute when the element is ready.
               * @param {object}   obj an optional object to be passed back as
               *                   a parameter to fn.
               * @param {boolean|object}  overrideContext If set to true, fn will execute
               *                   in the context of obj.  If an object, fn will
               *                   exectute in the context of that object
               *
               * @static
               */
              onContentReady: function(id, fn, obj, overrideContext) {
                  this.onAvailable(id, fn, obj, overrideContext, true);
              },
  
              /**
               * Executes the supplied callback when the DOM is first usable.  This
               * will execute immediately if called after the DOMReady event has
               * fired.   @todo the DOMContentReady event does not fire when the
               * script is dynamically injected into the page.  This means the
               * DOMReady custom event will never fire in FireFox or Opera when the
               * library is injected.  It _will_ fire in Safari, and the IE
               * implementation would allow for us to fire it if the defered script
               * is not available.  We want this to behave the same in all browsers.
               * Is there a way to identify when the script has been injected
               * instead of included inline?  Is there a way to know whether the
               * window onload event has fired without having had a listener attached
               * to it when it did so?
               *
               * <p>The callback is a CustomEvent, so the signature is:</p>
               * <p>type &lt;string&gt;, args &lt;array&gt;, customobject &lt;object&gt;</p>
               * <p>For DOMReady events, there are no fire argments, so the
               * signature is:</p>
               * <p>"DOMReady", [], obj</p>
               *
               *
               * @method onDOMReady
               *
               * @param {function} fn what to execute when the element is found.
               * @param {object}   obj an optional object to be passed back as
               *                   a parameter to fn.
               * @param {boolean|object}  overrideContext If set to true, fn will execute
               *                   in the context of obj, if set to an object it
               *                   will execute in the context of that object
               *
               * @static
               */
              // onDOMReady: function(fn, obj, overrideContext) {
              onDOMReady: function() {
                  this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments);
              },
  
  
              /**
               * Appends an event handler
               *
               * @method _addListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to assign the
               *  listener to.
               * @param {String}   sType     The type of event to append
               * @param {Function} fn        The method the event invokes
               * @param {Object}   obj    An arbitrary object that will be
               *                             passed as a parameter to the handler
               * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
               *                             the execution context of the listener. If an
               *                             object, this object becomes the execution
               *                             context.
               * @param {boolen}      capture capture or bubble phase
               * @return {Boolean} True if the action was successful or defered,
               *                        false if one or more of the elements
               *                        could not have the listener attached,
               *                        or if the operation throws an exception.
               * @private
               * @static
               */
              _addListener: function(el, sType, fn, obj, overrideContext, bCapture) {
  
                  if (!fn || !fn.call) {
                      YAHOO.log(sType + " addListener failed, invalid callback", "error", "Event");
                      return false;
                  }
  
                  // The el argument can be an array of elements or element ids.
                  if ( this._isValidCollection(el)) {
                      var ok = true;
                      for (var i=0,len=el.length; i<len; ++i) {
                          ok = this.on(el[i],
                                         sType,
                                         fn,
                                         obj,
                                         overrideContext) && ok;
                      }
                      return ok;
  
                  } else if (YAHOO.lang.isString(el)) {
                      var oEl = this.getEl(el);
                      // 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 (oEl) {
                          el = oEl;
                      } else {
                          // defer adding the event until the element is available
                          this.onAvailable(el, function() {
                             YAHOO.util.Event._addListener(el, sType, fn, obj, overrideContext, bCapture);
                          });
  
                          return true;
                      }
                  }
  
                  // Element should be an html element or an array if we get
                  // here.
                  if (!el) {
                      // this.logger.debug("unable to attach event " + sType);
                      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 && obj !== this) {
                      unloadListeners[unloadListeners.length] =
                              [el, sType, fn, obj, overrideContext];
                      return true;
                  }
  
                  // this.logger.debug("Adding handler: " + el + ", " + sType);
  
                  // if the user chooses to override the context, we use the custom
                  // object passed in, otherwise the executing context will be the
                  // HTML element that the event is registered on
                  var context = el;
                  if (overrideContext) {
                      if (overrideContext === true) {
                          context = obj;
                      } else {
                          context = overrideContext;
                      }
                  }
  
                  // wrap the function so we can return the obj object when
                  // the event fires;
                  var wrappedFn = function(e) {
                          return fn.call(context, YAHOO.util.Event.getEvent(e, el),
                                  obj);
                      };
  
                  var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture];
                  var index = listeners.length;
                  // cache the listener so we can try to automatically unload
                  listeners[index] = li;
  
                  try {
                      this._simpleAdd(el, sType, wrappedFn, bCapture);
                  } catch(ex) {
                      // handle an error trying to attach an event.  If it fails
                      // we need to clean up the cache
                      this.lastError = ex;
                      this.removeListener(el, sType, fn);
                      return false;
                  }
  
                  return true;
  
              },
  
              /**
               * Checks to see if the type requested is a special type
               * (as defined by the _specialTypes hash), and (if so) returns
               * the special type name.
               *
               * @method _getType
               *
               * @param {String}   sType     The type to look up
               * @private
               */
              _getType: function (type) {
  
                  return this._specialTypes[type] || type;
  
              },
  
  
              /**
               * Appends an event handler
               *
               * @method addListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to assign the
               *  listener to.
               * @param {String}   sType     The type of event to append
               * @param {Function} fn        The method the event invokes
               * @param {Object}   obj    An arbitrary object that will be
               *                             passed as a parameter to the handler
               * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
               *                             the execution context of the listener. If an
               *                             object, this object becomes the execution
               *                             context.
               * @return {Boolean} True if the action was successful or defered,
               *                        false if one or more of the elements
               *                        could not have the listener attached,
               *                        or if the operation throws an exception.
               * @static
               */
              addListener: function (el, sType, fn, obj, overrideContext) {
  
                  var capture = ((sType == FOCUSIN || sType == FOCUSOUT) && !YAHOO.env.ua.ie) ? true : false;
  
                  return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture);
  
              },
  
  
              /**
               * Attaches a focusin event listener to the specified element for
               * the purpose of listening for the focus event on the element's
               * descendants.
               * @method addFocusListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to assign the
               *  listener to.
               * @param {Function} fn        The method the event invokes
               * @param {Object}   obj    An arbitrary object that will be
               *                             passed as a parameter to the handler
               * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
               *                             the execution context of the listener. If an
               *                             object, this object becomes the execution
               *                             context.
               * @return {Boolean} True if the action was successful or defered,
               *                        false if one or more of the elements
               *                        could not have the listener attached,
               *                        or if the operation throws an exception.
               * @static
              * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
               */
              addFocusListener: function (el, fn, obj, overrideContext) {
                  return this.on(el, FOCUSIN, fn, obj, overrideContext);
              },
  
  
              /**
               * Removes a focusin event listener to the specified element for
               * the purpose of listening for the focus event on the element's
               * descendants.
               *
               * @method removeFocusListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to remove
               *  the listener from.
               * @param {Function} fn the method the event invokes.  If fn is
               *  undefined, then all event handlers for the type of event are
               *  removed.
               * @return {boolean} true if the unbind was successful, false
               *  otherwise.
               * @static
               * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type.
               */
              removeFocusListener: function (el, fn) {
                  return this.removeListener(el, FOCUSIN, fn);
              },
  
              /**
               * Attaches a focusout event listener to the specified element for
               * the purpose of listening for the blur event on the element's
               * descendants.
               *
               * @method addBlurListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to assign the
               *  listener to.
               * @param {Function} fn        The method the event invokes
               * @param {Object}   obj    An arbitrary object that will be
               *                             passed as a parameter to the handler
               * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
               *                             the execution context of the listener. If an
               *                             object, this object becomes the execution
               *                             context.
               * @return {Boolean} True if the action was successful or defered,
               *                        false if one or more of the elements
               *                        could not have the listener attached,
               *                        or if the operation throws an exception.
               * @static
               * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
               */
              addBlurListener: function (el, fn, obj, overrideContext) {
                  return this.on(el, FOCUSOUT, fn, obj, overrideContext);
              },
  
              /**
               * Removes a focusout event listener to the specified element for
               * the purpose of listening for the blur event on the element's
               * descendants.
               *
               * @method removeBlurListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to remove
               *  the listener from.
               * @param {Function} fn the method the event invokes.  If fn is
               *  undefined, then all event handlers for the type of event are
               *  removed.
               * @return {boolean} true if the unbind was successful, false
               *  otherwise.
               * @static
               * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type.
               */
              removeBlurListener: function (el, fn) {
                  return this.removeListener(el, FOCUSOUT, fn);
              },
  
              /**
               * Removes an event listener
               *
               * @method removeListener
               *
               * @param {String|HTMLElement|Array|NodeList} el An id, an element
               *  reference, or a collection of ids and/or elements to remove
               *  the listener from.
               * @param {String} sType the type of event to remove.
               * @param {Function} fn the method the event invokes.  If fn is
               *  undefined, then all event handlers for the type of event are
               *  removed.
               * @return {boolean} true if the unbind was successful, false
               *  otherwise.
               * @static
               */
              removeListener: function(el, sType, fn) {
                  var i, len, li;
  
                  sType = this._getType(sType);
  
                  // 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 (i=el.length-1; i>-1; i--) {
                          ok = ( this.removeListener(el[i], sType, fn) && ok );
                      }
                      return ok;
                  }
  
                  if (!fn || !fn.call) {
                      // this.logger.debug("Error, function is not valid " + fn);
                      //return false;
                      return this.purgeElement(el, false, sType);
                  }
  
                  if ("unload" == sType) {
  
                      for (i=unloadListeners.length-1; i>-1; i--) {
                          li = unloadListeners[i];
                          if (li &&
                              li[0] == el &&
                              li[1] == sType &&
                              li[2] == fn) {
                                  unloadListeners.splice(i, 1);
                                  // unloadListeners[i]=null;
                                  return true;
                          }
                      }
  
                      return false;
                  }
  
                  var cacheItem = null;
  
                  // The index is a hidden parameter; needed to remove it from
                  // the method signature because it was tempting users to
                  // try and take advantage of it, which is not possible.
                  var index = arguments[3];
  
                  if ("undefined" === typeof index) {
                      index = this._getCacheIndex(listeners, el, sType, fn);
                  }
  
                  if (index >= 0) {
                      cacheItem = listeners[index];
                  }
  
                  if (!el || !cacheItem) {
                      // this.logger.debug("cached listener not found");
                      return false;
                  }
  
                  // this.logger.debug("Removing handler: " + el + ", " + sType);
  
                  var bCapture = cacheItem[this.CAPTURE] === true ? true : false;
  
                  try {
                      this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture);
                  } catch(ex) {
                      this.lastError = ex;
                      return false;
                  }
  
                  // removed the wrapped handler
                  delete listeners[index][this.WFN];
                  delete listeners[index][this.FN];
                  listeners.splice(index, 1);
                  // listeners[index]=null;
  
                  return true;
  
              },
  
              /**
               * Returns the event's target element.  Safari sometimes provides
               * a text node, and this is automatically resolved to the text
               * node's parent so that it behaves like other browsers.
               * @method getTarget
               * @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.  @deprecated, the text node is
               *                  now resolved automatically
               * @return {HTMLElement} the event's target
               * @static
               */
              getTarget: function(ev, resolveTextNode) {
                  var t = ev.target || ev.srcElement;
                  return this.resolveTextNode(t);
              },
  
              /**
               * In some cases, some browsers will return a text node inside
               * the actual element that was targeted.  This normalizes the
               * return value for getTarget and getRelatedTarget.
               *
               * If accessing a property of the node throws an error, this is
               * probably the anonymous div wrapper Gecko adds inside text
               * nodes.  This likely will only occur when attempting to access
               * the relatedTarget.  In this case, we now return null because
               * the anonymous div is completely useless and we do not know
               * what the related target was because we can't even get to
               * the element's parent node.
               *
               * @method resolveTextNode
               * @param {HTMLElement} node node to resolve
               * @return {HTMLElement} the normized node
               * @static
               */
              resolveTextNode: function(n) {
                  try {
                      if (n && 3 == n.nodeType) {
                          return n.parentNode;
                      }
                  } catch(e) {
                      return null;
                  }
  
                  return n;
              },
  
              /**
               * Returns the event's pageX
               * @method getPageX
               * @param {Event} ev the event
               * @return {int} the event's pageX
               * @static
               */
              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
               * @method getPageY
               * @param {Event} ev the event
               * @return {int} the event's pageY
               * @static
               */
              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 pageX and pageY properties as an indexed array.
               * @method getXY
               * @param {Event} ev the event
               * @return {[x, y]} the pageX and pageY properties of the event
               * @static
               */
              getXY: function(ev) {
                  return [this.getPageX(ev), this.getPageY(ev)];
              },
  
              /**
               * Returns the event's related target
               * @method getRelatedTarget
               * @param {Event} ev the event
               * @return {HTMLElement} the event's relatedTarget
               * @static
               */
              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 this.resolveTextNode(t);
              },
  
              /**
               * Returns the time of the event.  If the time is not included, the
               * event is modified using the current time.
               * @method getTime
               * @param {Event} ev the event
               * @return {Date} the time of the event
               * @static
               */
              getTime: function(ev) {
                  if (!ev.time) {
                      var t = new Date().getTime();
                      try {
                          ev.time = t;
                      } catch(ex) {
                          this.lastError = ex;
                          return t;
                      }
                  }
  
                  return ev.time;
              },
  
              /**
               * Convenience method for stopPropagation + preventDefault
               * @method stopEvent
               * @param {Event} ev the event
               * @static
               */
              stopEvent: function(ev) {
                  this.stopPropagation(ev);
                  this.preventDefault(ev);
              },
  
              /**
               * Stops event propagation
               * @method stopPropagation
               * @param {Event} ev the event
               * @static
               */
              stopPropagation: function(ev) {
                  if (ev.stopPropagation) {
                      ev.stopPropagation();
                  } else {
                      ev.cancelBubble = true;
                  }
              },
  
              /**
               * Prevents the default behavior of the event
               * @method preventDefault
               * @param {Event} ev the event
               * @static
               */
              preventDefault: function(ev) {
                  if (ev.preventDefault) {
                      ev.preventDefault();
                  } else {
                      ev.returnValue = false;
                  }
              },
  
              /**
               * Finds the event in the window object, the caller's arguments, or
               * in the arguments of another method in the callstack.  This is
               * executed automatically for events registered through the event
               * manager, so the implementer should not normally need to execute
               * this function at all.
               * @method getEvent
               * @param {Event} e the event parameter from the handler
               * @param {HTMLElement} boundEl the element the listener is attached to
               * @return {Event} the event
               * @static
               */
              getEvent: function(e, boundEl) {
                  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
               * @method getCharCode
               * @param {Event} ev the event
               * @return {int} the event's charCode
               * @static
               */
              getCharCode: function(ev) {
                  var code = ev.keyCode || ev.charCode || 0;
  
                  // webkit key normalization
                  if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
                      code = webkitKeymap[code];
                  }
                  return code;
              },
  
              /**
               * Locating the saved event handler data by function ref
               *
               * @method _getCacheIndex
               * @static
               * @private
               */
              _getCacheIndex: function(a, el, sType, fn) {
                  for (var i=0, l=a.length; i<l; i=i+1) {
                      var li = a[i];
                      if ( li                 &&
                           li[this.FN] == fn  &&
                           li[this.EL] == el  &&
                           li[this.TYPE] == sType ) {
                          return i;
                      }
                  }
  
                  return -1;
              },
  
              /**
               * Generates an unique ID for the element if it does not already
               * have one.
               * @method generateId
               * @param el the element to create the id for
               * @return {string} the resulting id of the element
               * @static
               */
              generateId: function(el) {
                  var id = el.id;
  
                  if (!id) {
                      id = "yuievtautoid-" + counter;
                      ++counter;
                      el.id = id;
                  }
  
                  return id;
              },
  
  
              /**
               * 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.
               * @method _isValidCollection
               * @param o the object to test
               * @return {boolean} true if the object is array-like and populated
               * @static
               * @private
               */
              _isValidCollection: function(o) {
                  try {
                      return ( o                     && // o is something
                               typeof o !== "string" && // o is not a string
                               o.length              && // o is indexed
                               !o.tagName            && // o is not an HTML element
                               !o.alert              && // o is not a window
                               typeof o[0] !== "undefined" );
                  } catch(ex) {
                      YAHOO.log("node access error (xframe?)", "warn");
                      return false;
                  }
  
              },
  
              /**
               * @private
               * @property elCache
               * DOM element cache
               * @static
               * @deprecated Elements are not cached due to issues that arise when
               * elements are removed and re-added
               */
              elCache: {},
  
              /**
               * We cache elements bound by id because when the unload event
               * fires, we can no longer use document.getElementById
               * @method getEl
               * @static
               * @private
               * @deprecated Elements are not cached any longer
               */
              getEl: function(id) {
                  return (typeof id === "string") ? document.getElementById(id) : id;
              },
  
              /**
               * Clears the element cache
               * @deprecated Elements are not cached any longer
               * @method clearCache
               * @static
               * @private
               */
              clearCache: function() { },
  
              /**
               * Custom event the fires when the dom is initially usable
               * @event DOMReadyEvent
               */
              DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1),
  
              /**
               * hook up any deferred listeners
               * @method _load
               * @static
               * @private
               */
              _load: function(e) {
  
                  if (!loadComplete) {
                      loadComplete = true;
                      var EU = YAHOO.util.Event;
  
                      // Just in case DOMReady did not go off for some reason
                      EU._ready();
  
                      // Available elements may not have been detected before the
                      // window load event fires. Try to find them now so that the
                      // the user is more likely to get the onAvailable notifications
                      // before the window load notification
                      EU._tryPreloadAttach();
  
                  }
              },
  
              /**
               * Fires the DOMReady event listeners the first time the document is
               * usable.
               * @method _ready
               * @static
               * @private
               */
              _ready: function(e) {
                  var EU = YAHOO.util.Event;
                  if (!EU.DOMReady) {
                      EU.DOMReady=true;
  
                      // Fire the content ready custom event
                      EU.DOMReadyEvent.fire();
  
                      // Remove the DOMContentLoaded (FF/Opera)
                      EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
                  }
              },
  
              /**
               * Polling function that runs before the onload event fires,
               * attempting to attach to DOM Nodes as soon as they are
               * available
               * @method _tryPreloadAttach
               * @static
               * @private
               */
              _tryPreloadAttach: function() {
  
                  if (onAvailStack.length === 0) {
                      retryCount = 0;
                      if (this._interval) {
                          // clearInterval(this._interval);
                          this._interval.cancel();
                          this._interval = null;
                      }
                      return;
                  }
  
                  if (this.locked) {
                      return;
                  }
  
                  if (this.isIE) {
                      // Hold off if DOMReady has not fired and check current
                      // readyState to protect against the IE operation aborted
                      // issue.
                      if (!this.DOMReady) {
                          this.startInterval();
                          return;
                      }
                  }
  
                  this.locked = true;
  
                  // this.logger.debug("tryPreloadAttach");
  
                  // 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;
                  if (!tryAgain) {
                      tryAgain = (retryCount > 0 && onAvailStack.length > 0);
                  }
  
                  // onAvailable
                  var notAvail = [];
  
                  var executeItem = function (el, item) {
                      var context = el;
                      if (item.overrideContext) {
                          if (item.overrideContext === true) {
                              context = item.obj;
                          } else {
                              context = item.overrideContext;
                          }
                      }
                      item.fn.call(context, item.obj);
                  };
  
                  var i, len, item, el, ready=[];
  
                  // onAvailable onContentReady
                  for (i=0, len=onAvailStack.length; i<len; i=i+1) {
                      item = onAvailStack[i];
                      if (item) {
                          el = this.getEl(item.id);
                          if (el) {
                              if (item.checkReady) {
                                  if (loadComplete || el.nextSibling || !tryAgain) {
                                      ready.push(item);
                                      onAvailStack[i] = null;
                                  }
                              } else {
                                  executeItem(el, item);
                                  onAvailStack[i] = null;
                              }
                          } else {
                              notAvail.push(item);
                          }
                      }
                  }
  
                  // make sure onContentReady fires after onAvailable
                  for (i=0, len=ready.length; i<len; i=i+1) {
                      item = ready[i];
                      executeItem(this.getEl(item.id), item);
                  }
  
  
                  retryCount--;
  
                  if (tryAgain) {
                      for (i=onAvailStack.length-1; i>-1; i--) {
                          item = onAvailStack[i];
                          if (!item || !item.id) {
                              onAvailStack.splice(i, 1);
                          }
                      }
  
                      this.startInterval();
                  } else {
                      if (this._interval) {
                          // clearInterval(this._interval);
                          this._interval.cancel();
                          this._interval = null;
                      }
                  }
  
                  this.locked = false;
  
              },
  
              /**
               * Removes all listeners attached to the given element via addListener.
               * Optionally, the node's children can also be purged.
               * Optionally, you can specify a specific type of event to remove.
               * @method purgeElement
               * @param {HTMLElement} el the element to purge
               * @param {boolean} recurse recursively purge this element's children
               * as well.  Use with caution.
               * @param {string} sType optional type of listener to purge. If
               * left out, all listeners will be removed
               * @static
               */
              purgeElement: function(el, recurse, sType) {
                  var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
                  var elListeners = this.getListeners(oEl, sType), i, len;
                  if (elListeners) {
                      for (i=elListeners.length-1; i>-1; i--) {
                          var l = elListeners[i];
                          this.removeListener(oEl, l.type, l.fn);
                      }
                  }
  
                  if (recurse && oEl && oEl.childNodes) {
                      for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
                          this.purgeElement(oEl.childNodes[i], recurse, sType);
                      }
                  }
              },
  
              /**
               * Returns all listeners attached to the given element via addListener.
               * Optionally, you can specify a specific type of event to return.
               * @method getListeners
               * @param el {HTMLElement|string} the element or element id to inspect
               * @param sType {string} optional type of listener to return. If
               * left out, all listeners will be returned
               * @return {Object} the listener. Contains the following fields:
               * &nbsp;&nbsp;type:   (string)   the type of event
               * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
               * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
               * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default context
               * &nbsp;&nbsp;scope: (boolean)  the derived context based on the adjust parameter
               * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
               * @static
               */
              getListeners: function(el, sType) {
                  var results=[], searchLists;
                  if (!sType) {
                      searchLists = [listeners, unloadListeners];
                  } else if (sType === "unload") {
                      searchLists = [unloadListeners];
                  } else {
                      sType = this._getType(sType);
                      searchLists = [listeners];
                  }
  
                  var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
  
                  for (var j=0;j<searchLists.length; j=j+1) {
                      var searchList = searchLists[j];
                      if (searchList) {
                          for (var i=0,len=searchList.length; i<len ; ++i) {
                              var l = searchList[i];
                              if ( l  && l[this.EL] === oEl &&
                                      (!sType || sType === l[this.TYPE]) ) {
                                  results.push({
                                      type:   l[this.TYPE],
                                      fn:     l[this.FN],
                                      obj:    l[this.OBJ],
                                      adjust: l[this.OVERRIDE],
                                      scope:  l[this.ADJ_SCOPE],
                                      index:  i
                                  });
                              }
                          }
                      }
                  }
  
                  return (results.length) ? results : null;
              },
  
              /**
               * Removes all listeners registered by pe.event.  Called
               * automatically during the unload event.
               * @method _unload
               * @static
               * @private
               */
              _unload: function(e) {
  
                  var EU = YAHOO.util.Event, i, j, l, len, index,
                           ul = unloadListeners.slice(), context;
  
                  // execute and clear stored unload listeners
                  for (i=0, len=unloadListeners.length; i<len; ++i) {
                      l = ul[i];
                      if (l) {
                          try {
                              context = window;
                              if (l[EU.ADJ_SCOPE]) {
                                  if (l[EU.ADJ_SCOPE] === true) {
                                      context = l[EU.UNLOAD_OBJ];
                                  } else {
                                      context = l[EU.ADJ_SCOPE];
                                  }
                              }
                              l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
                          } catch(e1) {}
                          ul[i] = null;
                      }
                  }
  
                  l = null;
                  context = null;
                  unloadListeners = null;
  
                  // Remove listeners to handle IE memory leaks
                  // 2.5.0 listeners are removed for all browsers again.  FireFox preserves
                  // at least some listeners between page refreshes, potentially causing
                  // errors during page load (mouseover listeners firing before they
                  // should if the user moves the mouse at the correct moment).
                  if (listeners) {
                      for (j=listeners.length-1; j>-1; j--) {
                          l = listeners[j];
                          if (l) {
                              try {
                                  EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j);
                              } catch(e2) {}
                          }
                      }
                      l=null;
                  }
  
                  try {
                      EU._simpleRemove(window, "unload", EU._unload);
                      EU._simpleRemove(window, "load", EU._load);
                  } catch(e3) {}
  
              },
  
              /**
               * Returns scrollLeft
               * @method _getScrollLeft
               * @static
               * @private
               */
              _getScrollLeft: function() {
                  return this._getScroll()[1];
              },
  
              /**
               * Returns scrollTop
               * @method _getScrollTop
               * @static
               * @private
               */
              _getScrollTop: function() {
                  return this._getScroll()[0];
              },
  
              /**
               * Returns the scrollTop and scrollLeft.  Used to calculate the
               * pageX and pageY in Internet Explorer
               * @method _getScroll
               * @static
               * @private
               */
              _getScroll: function() {
                  var dd = document.documentElement, db = document.body;
                  if (dd && (dd.scrollTop || dd.scrollLeft)) {
                      return [dd.scrollTop, dd.scrollLeft];
                  } else if (db) {
                      return [db.scrollTop, db.scrollLeft];
                  } else {
                      return [0, 0];
                  }
              },
  
              /**
               * Used by old versions of CustomEvent, restored for backwards
               * compatibility
               * @method regCE
               * @private
               * @static
               * @deprecated still here for backwards compatibility
               */
              regCE: function() {},
  
              /**
               * Adds a DOM event directly without the caching, cleanup, context adj, etc
               *
               * @method _simpleAdd
               * @param {HTMLElement} el      the element to bind the handler to
               * @param {string}      sType   the type of event handler
               * @param {function}    fn      the callback to invoke
               * @param {boolen}      capture capture or bubble phase
               * @static
               * @private
               */
              _simpleAdd: function () {
                  if (window.addEventListener) {
                      return function(el, sType, fn, capture) {
                          el.addEventListener(sType, fn, (capture));
                      };
                  } else if (window.attachEvent) {
                      return function(el, sType, fn, capture) {
                          el.attachEvent("on" + sType, fn);
                      };
                  } else {
                      return function(){};
                  }
              }(),
  
              /**
               * Basic remove listener
               *
               * @method _simpleRemove
               * @param {HTMLElement} el      the element to bind the handler to
               * @param {string}      sType   the type of event handler
               * @param {function}    fn      the callback to invoke
               * @param {boolen}      capture capture or bubble phase
               * @static
               * @private
               */
              _simpleRemove: function() {
                  if (window.removeEventListener) {
                      return function (el, sType, fn, capture) {
                          el.removeEventListener(sType, fn, (capture));
                      };
                  } else if (window.detachEvent) {
                      return function (el, sType, fn) {
                          el.detachEvent("on" + sType, fn);
                      };
                  } else {
                      return function(){};
                  }
              }()
          };
  
      }();
  
      (function() {
          var EU = YAHOO.util.Event;
  
          /**
           * Appends an event handler.  This is an alias for <code>addListener</code>
           *
           * @method on
           *
           * @param {String|HTMLElement|Array|NodeList} el An id, an element
           *  reference, or a collection of ids and/or elements to assign the
           *  listener to.
           * @param {String}   sType     The type of event to append
           * @param {Function} fn        The method the event invokes
           * @param {Object}   obj    An arbitrary object that will be
           *                             passed as a parameter to the handler
           * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
           *                             the execution context of the listener. If an
           *                             object, this object becomes the execution
           *                             context.
           * @return {Boolean} True if the action was successful or defered,
           *                        false if one or more of the elements
           *                        could not have the listener attached,
           *                        or if the operation throws an exception.
           * @static
           */
          EU.on = EU.addListener;
  
          /**
           * YAHOO.util.Event.onFocus is an alias for addFocusListener
           * @method onFocus
           * @see addFocusListener
           * @static
           * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
           */
          EU.onFocus = EU.addFocusListener;
  
          /**
           * YAHOO.util.Event.onBlur is an alias for addBlurListener
           * @method onBlur
           * @see addBlurListener
           * @static
           * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
           */
          EU.onBlur = EU.addBlurListener;
  
  /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
  
          // Internet Explorer: use the readyState of a defered script.
          // This isolates what appears to be a safe moment to manipulate
          // the DOM prior to when the document's readyState suggests
          // it is safe to do so.
          if (EU.isIE) {
              if (self !== self.top) {
                  document.onreadystatechange = function() {
                      if (document.readyState == 'complete') {
                          document.onreadystatechange = null;
                          EU._ready();
                      }
                  };
              } else {
  
                  // Process onAvailable/onContentReady items when the
                  // DOM is ready.
                  YAHOO.util.Event.onDOMReady(
                          YAHOO.util.Event._tryPreloadAttach,
                          YAHOO.util.Event, true);
  
                  var n = document.createElement('p');
  
                  EU._dri = setInterval(function() {
                      try {
                          // throws an error if doc is not ready
                          n.doScroll('left');
                          clearInterval(EU._dri);
                          EU._dri = null;
                          EU._ready();
                          n = null;
                      } catch (ex) {
                      }
                  }, EU.POLL_INTERVAL);
              }
  
          // The document's readyState in Safari currently will
          // change to loaded/complete before images are loaded.
          } else if (EU.webkit && EU.webkit < 525) {
  
              EU._dri = setInterval(function() {
                  var rs=document.readyState;
                  if ("loaded" == rs || "complete" == rs) {
                      clearInterval(EU._dri);
                      EU._dri = null;
                      EU._ready();
                  }
              }, EU.POLL_INTERVAL);
  
          // FireFox and Opera: These browsers provide a event for this
          // moment.  The latest WebKit releases now support this event.
          } else {
  
              EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
  
          }
          /////////////////////////////////////////////////////////////
  
  
          EU._simpleAdd(window, "load", EU._load);
          EU._simpleAdd(window, "unload", EU._unload);
          EU._tryPreloadAttach();
      })();
  
  }
  /**
   * EventProvider is designed to be used with YAHOO.augment to wrap
   * CustomEvents in an interface that allows events to be subscribed to
   * and fired by name.  This makes it possible for implementing code to
   * subscribe to an event that either has not been created yet, or will
   * not be created at all.
   *
   * @Class EventProvider
   */
  YAHOO.util.EventProvider = function() { };
  
  YAHOO.util.EventProvider.prototype = {
  
      /**
       * Private storage of custom events
       * @property __yui_events
       * @type Object[]
       * @private
       */
      __yui_events: null,
  
      /**
       * Private storage of custom event subscribers
       * @property __yui_subscribers
       * @type Object[]
       * @private
       */
      __yui_subscribers: null,
  
      /**
       * Subscribe to a CustomEvent by event type
       *
       * @method subscribe
       * @param p_type     {string}   the type, or name of the event
       * @param p_fn       {function} the function to exectute when the event fires
       * @param p_obj      {Object}   An object to be passed along when the event
       *                              fires
       * @param overrideContext {boolean}  If true, the obj passed in becomes the
       *                              execution scope of the listener
       */
      subscribe: function(p_type, p_fn, p_obj, overrideContext) {
  
          this.__yui_events = this.__yui_events || {};
          var ce = this.__yui_events[p_type];
  
          if (ce) {
              ce.subscribe(p_fn, p_obj, overrideContext);
          } else {
              this.__yui_subscribers = this.__yui_subscribers || {};
              var subs = this.__yui_subscribers;
              if (!subs[p_type]) {
                  subs[p_type] = [];
              }
              subs[p_type].push(
                  { fn: p_fn, obj: p_obj, overrideContext: overrideContext } );
          }
      },
  
      /**
       * Unsubscribes one or more listeners the from the specified event
       * @method unsubscribe
       * @param p_type {string}   The type, or name of the event.  If the type
       *                          is not specified, it will attempt to remove
       *                          the listener from all hosted events.
       * @param p_fn   {Function} The subscribed function to unsubscribe, if not
       *                          supplied, all subscribers will be removed.
       * @param p_obj  {Object}   The custom object passed to subscribe.  This is
       *                        optional, but if supplied will be used to
       *                        disambiguate multiple listeners that are the same
       *                        (e.g., you subscribe many object using a function
       *                        that lives on the prototype)
       * @return {boolean} true if the subscriber was found and detached.
       */
      unsubscribe: function(p_type, p_fn, p_obj) {
          this.__yui_events = this.__yui_events || {};
          var evts = this.__yui_events;
          if (p_type) {
              var ce = evts[p_type];
              if (ce) {
                  return ce.unsubscribe(p_fn, p_obj);
              }
          } else {
              var ret = true;
              for (var i in evts) {
                  if (YAHOO.lang.hasOwnProperty(evts, i)) {
                      ret = ret && evts[i].unsubscribe(p_fn, p_obj);
                  }
              }
              return ret;
          }
  
          return false;
      },
  
      /**
       * Removes all listeners from the specified event.  If the event type
       * is not specified, all listeners from all hosted custom events will
       * be removed.
       * @method unsubscribeAll
       * @param p_type {string}   The type, or name of the event
       */
      unsubscribeAll: function(p_type) {
          return this.unsubscribe(p_type);
      },
  
      /**
       * Creates a new custom event of the specified type.  If a custom event
       * by that name already exists, it will not be re-created.  In either
       * case the custom event is returned.
       *
       * @method createEvent
       *
       * @param p_type {string} the type, or name of the event
       * @param p_config {object} optional config params.  Valid properties are:
       *
       *  <ul>
       *    <li>
       *      scope: defines the default execution scope.  If not defined
       *      the default scope will be this instance.
       *    </li>
       *    <li>
       *      silent: if true, the custom event will not generate log messages.
       *      This is false by default.
       *    </li>
       *    <li>
       *      fireOnce: if true, the custom event will only notify subscribers
       *      once regardless of the number of times the event is fired.  In
       *      addition, new subscribers will be executed immediately if the
       *      event has already fired.
       *      This is false by default.
       *    </li>
       *    <li>
       *      onSubscribeCallback: specifies a callback to execute when the
       *      event has a new subscriber.  This will fire immediately for
       *      each queued subscriber if any exist prior to the creation of
       *      the event.
       *    </li>
       *  </ul>
       *
       *  @return {CustomEvent} the custom event
       *
       */
      createEvent: function(p_type, p_config) {
  
          this.__yui_events = this.__yui_events || {};
          var opts = p_config || {},
              events = this.__yui_events, ce;
  
          if (events[p_type]) {
  YAHOO.log("EventProvider createEvent skipped: '"+p_type+"' already exists");
          } else {
  
              ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent,
                           YAHOO.util.CustomEvent.FLAT, opts.fireOnce);
  
              events[p_type] = ce;
  
              if (opts.onSubscribeCallback) {
                  ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
              }
  
              this.__yui_subscribers = this.__yui_subscribers || {};
              var qs = this.__yui_subscribers[p_type];
  
              if (qs) {
                  for (var i=0; i<qs.length; ++i) {
                      ce.subscribe(qs[i].fn, qs[i].obj, qs[i].overrideContext);
                  }
              }
          }
  
          return events[p_type];
      },
  
  
     /**
       * Fire a custom event by name.  The callback functions will be executed
       * from the scope specified when the event was created, and with the
       * following parameters:
       *   <ul>
       *   <li>The first argument fire() was executed with</li>
       *   <li>The custom object (if any) that was passed into the subscribe()
       *       method</li>
       *   </ul>
       * @method fireEvent
       * @param p_type    {string}  the type, or name of the event
       * @param arguments {Object*} an arbitrary set of parameters to pass to
       *                            the handler.
       * @return {boolean} the return value from CustomEvent.fire
       *
       */
      fireEvent: function(p_type) {
  
          this.__yui_events = this.__yui_events || {};
          var ce = this.__yui_events[p_type];
  
          if (!ce) {
  YAHOO.log(p_type + "event fired before it was created.");
              return null;
          }
  
          var args = [];
          for (var i=1; i<arguments.length; ++i) {
              args.push(arguments[i]);
          }
          return ce.fire.apply(ce, args);
      },
  
      /**
       * Returns true if the custom event of the provided type has been created
       * with createEvent.
       * @method hasEvent
       * @param type {string} the type, or name of the event
       */
      hasEvent: function(type) {
          if (this.__yui_events) {
              if (this.__yui_events[type]) {
                  return true;
              }
          }
          return false;
      }
  
  };
  
  (function() {
  
      var Event = YAHOO.util.Event, Lang = YAHOO.lang;
  
  /**
  * KeyListener is a utility that provides an easy interface for listening for
  * keydown/keyup events fired against DOM elements.
  * @namespace YAHOO.util
  * @class KeyListener
  * @constructor
  * @param {HTMLElement} attachTo The element or element ID to which the key
  *                               event should be attached
  * @param {String}      attachTo The element or element ID to which the key
  *                               event should be attached
  * @param {Object}      keyData  The object literal representing the key(s)
  *                               to detect. Possible attributes are
  *                               shift(boolean), alt(boolean), ctrl(boolean)
  *                               and keys(either an int or an array of ints
  *                               representing keycodes).
  * @param {Function}    handler  The CustomEvent handler to fire when the
  *                               key event is detected
  * @param {Object}      handler  An object literal representing the handler.
  * @param {String}      event    Optional. The event (keydown or keyup) to
  *                               listen for. Defaults automatically to keydown.
  *
  * @knownissue the "keypress" event is completely broken in Safari 2.x and below.
  *             the workaround is use "keydown" for key listening.  However, if
  *             it is desired to prevent the default behavior of the keystroke,
  *             that can only be done on the keypress event.  This makes key
  *             handling quite ugly.
  * @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
  *             There currently is no workaround other than choosing another
  *             key to listen for.
  */
  YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
      if (!attachTo) {
          YAHOO.log("No attachTo element specified", "error");
      } else if (!keyData) {
          YAHOO.log("No keyData specified", "error");
      } else if (!handler) {
          YAHOO.log("No handler specified", "error");
      }
  
      if (!event) {
          event = YAHOO.util.KeyListener.KEYDOWN;
      }
  
      /**
      * The CustomEvent fired internally when a key is pressed
      * @event keyEvent
      * @private
      * @param {Object} keyData The object literal representing the key(s) to
      *                         detect. Possible attributes are shift(boolean),
      *                         alt(boolean), ctrl(boolean) and keys(either an
      *                         int or an array of ints representing keycodes).
      */
      var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
  
      /**
      * The CustomEvent fired when the KeyListener is enabled via the enable()
      * function
      * @event enabledEvent
      * @param {Object} keyData The object literal representing the key(s) to
      *                         detect. Possible attributes are shift(boolean),
      *                         alt(boolean), ctrl(boolean) and keys(either an
      *                         int or an array of ints representing keycodes).
      */
      this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
  
      /**
      * The CustomEvent fired when the KeyListener is disabled via the
      * disable() function
      * @event disabledEvent
      * @param {Object} keyData The object literal representing the key(s) to
      *                         detect. Possible attributes are shift(boolean),
      *                         alt(boolean), ctrl(boolean) and keys(either an
      *                         int or an array of ints representing keycodes).
      */
      this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
  
      if (Lang.isString(attachTo)) {
          attachTo = document.getElementById(attachTo); // No Dom util
      }
  
      if (Lang.isFunction(handler)) {
          keyEvent.subscribe(handler);
      } else {
          keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
      }
  
      /**
      * Handles the key event when a key is pressed.
      * @method handleKeyPress
      * @param {DOMEvent} e   The keypress DOM event
      * @param {Object}   obj The DOM event scope object
      * @private
      */
      function handleKeyPress(e, obj) {
          if (! keyData.shift) {
              keyData.shift = false;
          }
          if (! keyData.alt) {
              keyData.alt = false;
          }
          if (! keyData.ctrl) {
              keyData.ctrl = false;
          }
  
          // check held down modifying keys first
          if (e.shiftKey == keyData.shift &&
              e.altKey   == keyData.alt &&
              e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
  
              var dataItem, keys = keyData.keys, key;
  
              if (YAHOO.lang.isArray(keys)) {
                  for (var i=0;i<keys.length;i++) {
                      dataItem = keys[i];
                      key = Event.getCharCode(e);
  
                      if (dataItem == key) {
                          keyEvent.fire(key, e);
                          break;
                      }
                  }
              } else {
                  key = Event.getCharCode(e);
                  if (keys == key ) {
                      keyEvent.fire(key, e);
                  }
              }
          }
      }
  
      /**
      * Enables the KeyListener by attaching the DOM event listeners to the
      * target DOM element
      * @method enable
      */
      this.enable = function() {
          if (! this.enabled) {
              Event.on(attachTo, event, handleKeyPress);
              this.enabledEvent.fire(keyData);
          }
          /**
          * Boolean indicating the enabled/disabled state of the Tooltip
          * @property enabled
          * @type Boolean
          */
          this.enabled = true;
      };
  
      /**
      * Disables the KeyListener by removing the DOM event listeners from the
      * target DOM element
      * @method disable
      */
      this.disable = function() {
          if (this.enabled) {
              Event.removeListener(attachTo, event, handleKeyPress);
              this.disabledEvent.fire(keyData);
          }
          this.enabled = false;
      };
  
      /**
      * Returns a String representation of the object.
      * @method toString
      * @return {String}  The string representation of the KeyListener
      */
      this.toString = function() {
          return "KeyListener [" + keyData.keys + "] " + attachTo.tagName +
                  (attachTo.id ? "[" + attachTo.id + "]" : "");
      };
  
  };
  
  var KeyListener = YAHOO.util.KeyListener;
  
  /**
   * Constant representing the DOM "keydown" event.
   * @property YAHOO.util.KeyListener.KEYDOWN
   * @static
   * @final
   * @type String
   */
  KeyListener.KEYDOWN = "keydown";
  
  /**
   * Constant representing the DOM "keyup" event.
   * @property YAHOO.util.KeyListener.KEYUP
   * @static
   * @final
   * @type String
   */
  KeyListener.KEYUP = "keyup";
  
  /**
   * keycode constants for a subset of the special keys
   * @property KEY
   * @static
   * @final
   */
  KeyListener.KEY = {
      ALT          : 18,
      BACK_SPACE   : 8,
      CAPS_LOCK    : 20,
      CONTROL      : 17,
      DELETE       : 46,
      DOWN         : 40,
      END          : 35,
      ENTER        : 13,
      ESCAPE       : 27,
      HOME         : 36,
      LEFT         : 37,
      META         : 224,
      NUM_LOCK     : 144,
      PAGE_DOWN    : 34,
      PAGE_UP      : 33,
      PAUSE        : 19,
      PRINTSCREEN  : 44,
      RIGHT        : 39,
      SCROLL_LOCK  : 145,
      SHIFT        : 16,
      SPACE        : 32,
      TAB          : 9,
      UP           : 38
  };
  
  })();
  YAHOO.register("event", YAHOO.util.Event, {version: "2.9.0", build: "2800"});
  