[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/2in3/2.9.0/build/yui2-autocomplete/ -> yui2-autocomplete-debug.js (source)

   1  YUI.add('yui2-autocomplete', function(Y) {
   2      var YAHOO    = Y.YUI2;
   3      /*
   4  Copyright (c) 2011, Yahoo! Inc. All rights reserved.
   5  Code licensed under the BSD License:
   6  http://developer.yahoo.com/yui/license.html
   7  version: 2.9.0
   8  */
   9  /////////////////////////////////////////////////////////////////////////////
  10  //
  11  // YAHOO.widget.DataSource Backwards Compatibility
  12  //
  13  /////////////////////////////////////////////////////////////////////////////
  14  
  15  YAHOO.widget.DS_JSArray = YAHOO.util.LocalDataSource;
  16  
  17  YAHOO.widget.DS_JSFunction = YAHOO.util.FunctionDataSource;
  18  
  19  YAHOO.widget.DS_XHR = function(sScriptURI, aSchema, oConfigs) {
  20      var DS = new YAHOO.util.XHRDataSource(sScriptURI, oConfigs);
  21      DS._aDeprecatedSchema = aSchema;
  22      return DS;
  23  };
  24  
  25  YAHOO.widget.DS_ScriptNode = function(sScriptURI, aSchema, oConfigs) {
  26      var DS = new YAHOO.util.ScriptNodeDataSource(sScriptURI, oConfigs);
  27      DS._aDeprecatedSchema = aSchema;
  28      return DS;
  29  };
  30  
  31  YAHOO.widget.DS_XHR.TYPE_JSON = YAHOO.util.DataSourceBase.TYPE_JSON;
  32  YAHOO.widget.DS_XHR.TYPE_XML = YAHOO.util.DataSourceBase.TYPE_XML;
  33  YAHOO.widget.DS_XHR.TYPE_FLAT = YAHOO.util.DataSourceBase.TYPE_TEXT;
  34  
  35  // TODO: widget.DS_ScriptNode.scriptCallbackParam
  36  
  37  
  38  
  39   /**
  40   * The AutoComplete control provides the front-end logic for text-entry suggestion and
  41   * completion functionality.
  42   *
  43   * @module autocomplete
  44   * @requires yahoo, dom, event, datasource
  45   * @optional animation
  46   * @namespace YAHOO.widget
  47   * @title AutoComplete Widget
  48   */
  49  
  50  /****************************************************************************/
  51  /****************************************************************************/
  52  /****************************************************************************/
  53  
  54  /**
  55   * The AutoComplete class provides the customizable functionality of a plug-and-play DHTML
  56   * auto completion widget.  Some key features:
  57   * <ul>
  58   * <li>Navigate with up/down arrow keys and/or mouse to pick a selection</li>
  59   * <li>The drop down container can "roll down" or "fly out" via configurable
  60   * animation</li>
  61   * <li>UI look-and-feel customizable through CSS, including container
  62   * attributes, borders, position, fonts, etc</li>
  63   * </ul>
  64   *
  65   * @class AutoComplete
  66   * @constructor
  67   * @param elInput {HTMLElement} DOM element reference of an input field.
  68   * @param elInput {String} String ID of an input field.
  69   * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
  70   * @param elContainer {String} String ID of an existing DIV.
  71   * @param oDataSource {YAHOO.widget.DataSource} DataSource instance.
  72   * @param oConfigs {Object} (optional) Object literal of configuration params.
  73   */
  74  YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
  75      if(elInput && elContainer && oDataSource) {
  76          // Validate DataSource
  77          if(oDataSource && YAHOO.lang.isFunction(oDataSource.sendRequest)) {
  78              this.dataSource = oDataSource;
  79          }
  80          else {
  81              YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString());
  82              return;
  83          }
  84  
  85          // YAHOO.widget.DataSource schema backwards compatibility
  86          // Converted deprecated schema into supported schema
  87          // First assume key data is held in position 0 of results array
  88          this.key = 0;
  89          var schema = oDataSource.responseSchema;
  90          // An old school schema has been defined in the deprecated DataSource constructor
  91          if(oDataSource._aDeprecatedSchema) {
  92              var aDeprecatedSchema = oDataSource._aDeprecatedSchema;
  93              if(YAHOO.lang.isArray(aDeprecatedSchema)) {
  94                  
  95                  if((oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_JSON) || 
  96                  (oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_UNKNOWN)) { // Used to default to unknown
  97                      // Store the resultsList
  98                      schema.resultsList = aDeprecatedSchema[0];
  99                      // Store the key
 100                      this.key = aDeprecatedSchema[1];
 101                      // Only resultsList and key are defined, so grab all the data
 102                      schema.fields = (aDeprecatedSchema.length < 3) ? null : aDeprecatedSchema.slice(1);
 103                  }
 104                  else if(oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_XML) {
 105                      schema.resultNode = aDeprecatedSchema[0];
 106                      this.key = aDeprecatedSchema[1];
 107                      schema.fields = aDeprecatedSchema.slice(1);
 108                  }                
 109                  else if(oDataSource.responseType === YAHOO.util.DataSourceBase.TYPE_TEXT) {
 110                      schema.recordDelim = aDeprecatedSchema[0];
 111                      schema.fieldDelim = aDeprecatedSchema[1];
 112                  }                
 113                  oDataSource.responseSchema = schema;
 114              }
 115          }
 116          
 117          // Validate input element
 118          if(YAHOO.util.Dom.inDocument(elInput)) {
 119              if(YAHOO.lang.isString(elInput)) {
 120                      this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
 121                      this._elTextbox = document.getElementById(elInput);
 122              }
 123              else {
 124                  this._sName = (elInput.id) ?
 125                      "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
 126                      "instance" + YAHOO.widget.AutoComplete._nIndex;
 127                  this._elTextbox = elInput;
 128              }
 129              YAHOO.util.Dom.addClass(this._elTextbox, "yui-ac-input");
 130          }
 131          else {
 132              YAHOO.log("Could not instantiate AutoComplete due to an invalid input element", "error", this.toString());
 133              return;
 134          }
 135  
 136          // Validate container element
 137          if(YAHOO.util.Dom.inDocument(elContainer)) {
 138              if(YAHOO.lang.isString(elContainer)) {
 139                      this._elContainer = document.getElementById(elContainer);
 140              }
 141              else {
 142                  this._elContainer = elContainer;
 143              }
 144              if(this._elContainer.style.display == "none") {
 145                  YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString());
 146              }
 147              
 148              // For skinning
 149              var elParent = this._elContainer.parentNode;
 150              var elTag = elParent.tagName.toLowerCase();
 151              if(elTag == "div") {
 152                  YAHOO.util.Dom.addClass(elParent, "yui-ac");
 153              }
 154              else {
 155                  YAHOO.log("Could not find the wrapper element for skinning", "warn", this.toString());
 156              }
 157          }
 158          else {
 159              YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString());
 160              return;
 161          }
 162  
 163          // Default applyLocalFilter setting is to enable for local sources
 164          if(this.dataSource.dataType === YAHOO.util.DataSourceBase.TYPE_LOCAL) {
 165              this.applyLocalFilter = true;
 166          }
 167          
 168          // Set any config params passed in to override defaults
 169          if(oConfigs && (oConfigs.constructor == Object)) {
 170              for(var sConfig in oConfigs) {
 171                  if(sConfig) {
 172                      this[sConfig] = oConfigs[sConfig];
 173                  }
 174              }
 175          }
 176  
 177          // Initialization sequence
 178          this._initContainerEl();
 179          this._initProps();
 180          this._initListEl();
 181          this._initContainerHelperEls();
 182  
 183          // Set up events
 184          var oSelf = this;
 185          var elTextbox = this._elTextbox;
 186  
 187          // Dom events
 188          YAHOO.util.Event.addListener(elTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
 189          YAHOO.util.Event.addListener(elTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
 190          YAHOO.util.Event.addListener(elTextbox,"focus",oSelf._onTextboxFocus,oSelf);
 191          YAHOO.util.Event.addListener(elTextbox,"blur",oSelf._onTextboxBlur,oSelf);
 192          YAHOO.util.Event.addListener(elContainer,"mouseover",oSelf._onContainerMouseover,oSelf);
 193          YAHOO.util.Event.addListener(elContainer,"mouseout",oSelf._onContainerMouseout,oSelf);
 194          YAHOO.util.Event.addListener(elContainer,"click",oSelf._onContainerClick,oSelf);
 195          YAHOO.util.Event.addListener(elContainer,"scroll",oSelf._onContainerScroll,oSelf);
 196          YAHOO.util.Event.addListener(elContainer,"resize",oSelf._onContainerResize,oSelf);
 197          YAHOO.util.Event.addListener(elTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
 198          YAHOO.util.Event.addListener(window,"unload",oSelf._onWindowUnload,oSelf);
 199  
 200          // Custom events
 201          this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
 202          this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
 203          this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
 204          this.dataRequestCancelEvent = new YAHOO.util.CustomEvent("dataRequestCancel", this);
 205          this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
 206          this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
 207          this.containerPopulateEvent = new YAHOO.util.CustomEvent("containerPopulate", this);
 208          this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
 209          this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
 210          this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
 211          this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
 212          this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
 213          this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
 214          this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
 215          this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
 216          this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
 217          this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
 218          this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
 219          this.textboxChangeEvent = new YAHOO.util.CustomEvent("textboxChange", this);
 220          
 221          // Finish up
 222          elTextbox.setAttribute("autocomplete","off");
 223          YAHOO.widget.AutoComplete._nIndex++;
 224          YAHOO.log("AutoComplete initialized","info",this.toString());
 225      }
 226      // Required arguments were not found
 227      else {
 228          YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString());
 229      }
 230  };
 231  
 232  /////////////////////////////////////////////////////////////////////////////
 233  //
 234  // Public member variables
 235  //
 236  /////////////////////////////////////////////////////////////////////////////
 237  
 238  /**
 239   * The DataSource object that encapsulates the data used for auto completion.
 240   * This object should be an inherited object from YAHOO.widget.DataSource.
 241   *
 242   * @property dataSource
 243   * @type YAHOO.widget.DataSource
 244   */
 245  YAHOO.widget.AutoComplete.prototype.dataSource = null;
 246  
 247  /**
 248   * By default, results from local DataSources will pass through the filterResults
 249   * method to apply a client-side matching algorithm. 
 250   * 
 251   * @property applyLocalFilter
 252   * @type Boolean
 253   * @default true for local arrays and json, otherwise false
 254   */
 255  YAHOO.widget.AutoComplete.prototype.applyLocalFilter = null;
 256  
 257  /**
 258   * When applyLocalFilter is true, the local filtering algorthim can have case sensitivity
 259   * enabled. 
 260   * 
 261   * @property queryMatchCase
 262   * @type Boolean
 263   * @default false
 264   */
 265  YAHOO.widget.AutoComplete.prototype.queryMatchCase = false;
 266  
 267  /**
 268   * When applyLocalFilter is true, results can  be locally filtered to return
 269   * matching strings that "contain" the query string rather than simply "start with"
 270   * the query string.
 271   * 
 272   * @property queryMatchContains
 273   * @type Boolean
 274   * @default false
 275   */
 276  YAHOO.widget.AutoComplete.prototype.queryMatchContains = false;
 277  
 278  /**
 279   * Enables query subset matching. When the DataSource's cache is enabled and queryMatchSubset is
 280   * true, substrings of queries will return matching cached results. For
 281   * instance, if the first query is for "abc" susequent queries that start with
 282   * "abc", like "abcd", will be queried against the cache, and not the live data
 283   * source. Recommended only for DataSources that return comprehensive results
 284   * for queries with very few characters.
 285   *
 286   * @property queryMatchSubset
 287   * @type Boolean
 288   * @default false
 289   *
 290   */
 291  YAHOO.widget.AutoComplete.prototype.queryMatchSubset = false;
 292  
 293  /**
 294   * Number of characters that must be entered before querying for results. A negative value
 295   * effectively turns off the widget. A value of 0 allows queries of null or empty string
 296   * values.
 297   *
 298   * @property minQueryLength
 299   * @type Number
 300   * @default 1
 301   */
 302  YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
 303  
 304  /**
 305   * Maximum number of results to display in results container.
 306   *
 307   * @property maxResultsDisplayed
 308   * @type Number
 309   * @default 10
 310   */
 311  YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
 312  
 313  /**
 314   * Number of seconds to delay before submitting a query request.  If a query
 315   * request is received before a previous one has completed its delay, the
 316   * previous request is cancelled and the new request is set to the delay. If 
 317   * typeAhead is also enabled, this value must always be less than the typeAheadDelay
 318   * in order to avoid certain race conditions. 
 319   *
 320   * @property queryDelay
 321   * @type Number
 322   * @default 0.2
 323   */
 324  YAHOO.widget.AutoComplete.prototype.queryDelay = 0.2;
 325  
 326  /**
 327   * If typeAhead is true, number of seconds to delay before updating input with
 328   * typeAhead value. In order to prevent certain race conditions, this value must
 329   * always be greater than the queryDelay.
 330   *
 331   * @property typeAheadDelay
 332   * @type Number
 333   * @default 0.5
 334   */
 335  YAHOO.widget.AutoComplete.prototype.typeAheadDelay = 0.5;
 336  
 337  /**
 338   * When IME usage is detected or interval detection is explicitly enabled,
 339   * AutoComplete will detect the input value at the given interval and send a
 340   * query if the value has changed.
 341   *
 342   * @property queryInterval
 343   * @type Number
 344   * @default 500
 345   */
 346  YAHOO.widget.AutoComplete.prototype.queryInterval = 500;
 347  
 348  /**
 349   * Class name of a highlighted item within results container.
 350   *
 351   * @property highlightClassName
 352   * @type String
 353   * @default "yui-ac-highlight"
 354   */
 355  YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
 356  
 357  /**
 358   * Class name of a pre-highlighted item within results container.
 359   *
 360   * @property prehighlightClassName
 361   * @type String
 362   */
 363  YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
 364  
 365  /**
 366   * Query delimiter. A single character separator for multiple delimited
 367   * selections. Multiple delimiter characteres may be defined as an array of
 368   * strings. A null value or empty string indicates that query results cannot
 369   * be delimited. This feature is not recommended if you need forceSelection to
 370   * be true.
 371   *
 372   * @property delimChar
 373   * @type String | String[]
 374   */
 375  YAHOO.widget.AutoComplete.prototype.delimChar = null;
 376  
 377  /**
 378   * Whether or not the first item in results container should be automatically highlighted
 379   * on expand.
 380   *
 381   * @property autoHighlight
 382   * @type Boolean
 383   * @default true
 384   */
 385  YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
 386  
 387  /**
 388   * If autohighlight is enabled, whether or not the input field should be automatically updated
 389   * with the first query result as the user types, auto-selecting the substring portion
 390   * of the first result that the user has not yet typed.
 391   *
 392   * @property typeAhead
 393   * @type Boolean
 394   * @default false
 395   */
 396  YAHOO.widget.AutoComplete.prototype.typeAhead = false;
 397  
 398  /**
 399   * Whether or not to animate the expansion/collapse of the results container in the
 400   * horizontal direction.
 401   *
 402   * @property animHoriz
 403   * @type Boolean
 404   * @default false
 405   */
 406  YAHOO.widget.AutoComplete.prototype.animHoriz = false;
 407  
 408  /**
 409   * Whether or not to animate the expansion/collapse of the results container in the
 410   * vertical direction.
 411   *
 412   * @property animVert
 413   * @type Boolean
 414   * @default true
 415   */
 416  YAHOO.widget.AutoComplete.prototype.animVert = true;
 417  
 418  /**
 419   * Speed of container expand/collapse animation, in seconds..
 420   *
 421   * @property animSpeed
 422   * @type Number
 423   * @default 0.3
 424   */
 425  YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
 426  
 427  /**
 428   * Whether or not to force the user's selection to match one of the query
 429   * results. Enabling this feature essentially transforms the input field into a
 430   * &lt;select&gt; field. This feature is not recommended with delimiter character(s)
 431   * defined.
 432   *
 433   * @property forceSelection
 434   * @type Boolean
 435   * @default false
 436   */
 437  YAHOO.widget.AutoComplete.prototype.forceSelection = false;
 438  
 439  /**
 440   * Whether or not to allow browsers to cache user-typed input in the input
 441   * field. Disabling this feature will prevent the widget from setting the
 442   * autocomplete="off" on the input field. When autocomplete="off"
 443   * and users click the back button after form submission, user-typed input can
 444   * be prefilled by the browser from its cache. This caching of user input may
 445   * not be desired for sensitive data, such as credit card numbers, in which
 446   * case, implementers should consider setting allowBrowserAutocomplete to false.
 447   *
 448   * @property allowBrowserAutocomplete
 449   * @type Boolean
 450   * @default true
 451   */
 452  YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
 453  
 454  /**
 455   * Enabling this feature prevents the toggling of the container to a collapsed state.
 456   * Setting to true does not automatically trigger the opening of the container.
 457   * Implementers are advised to pre-load the container with an explicit "sendQuery()" call.   
 458   *
 459   * @property alwaysShowContainer
 460   * @type Boolean
 461   * @default false
 462   */
 463  YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
 464  
 465  /**
 466   * Whether or not to use an iFrame to layer over Windows form elements in
 467   * IE. Set to true only when the results container will be on top of a
 468   * &lt;select&gt; field in IE and thus exposed to the IE z-index bug (i.e.,
 469   * 5.5 < IE < 7).
 470   *
 471   * @property useIFrame
 472   * @type Boolean
 473   * @default false
 474   */
 475  YAHOO.widget.AutoComplete.prototype.useIFrame = false;
 476  
 477  /**
 478   * Whether or not the results container should have a shadow.
 479   *
 480   * @property useShadow
 481   * @type Boolean
 482   * @default false
 483   */
 484  YAHOO.widget.AutoComplete.prototype.useShadow = false;
 485  
 486  /**
 487   * Whether or not the input field should be updated with selections.
 488   *
 489   * @property suppressInputUpdate
 490   * @type Boolean
 491   * @default false
 492   */
 493  YAHOO.widget.AutoComplete.prototype.suppressInputUpdate = false;
 494  
 495  /**
 496   * For backward compatibility to pre-2.6.0 formatResults() signatures, setting
 497   * resultsTypeList to true will take each object literal result returned by
 498   * DataSource and flatten into an array.  
 499   *
 500   * @property resultTypeList
 501   * @type Boolean
 502   * @default true
 503   */
 504  YAHOO.widget.AutoComplete.prototype.resultTypeList = true;
 505  
 506  /**
 507   * For XHR DataSources, AutoComplete will automatically insert a "?" between the server URI and 
 508   * the "query" param/value pair. To prevent this behavior, implementers should
 509   * set this value to false. To more fully customize the query syntax, implementers
 510   * should override the generateRequest() method.
 511   *
 512   * @property queryQuestionMark
 513   * @type Boolean
 514   * @default true
 515   */
 516  YAHOO.widget.AutoComplete.prototype.queryQuestionMark = true;
 517  
 518  /**
 519   * If true, before each time the container expands, the container element will be
 520   * positioned to snap to the bottom-left corner of the input element. If
 521   * autoSnapContainer is set to false, this positioning will not be done.  
 522   *
 523   * @property autoSnapContainer
 524   * @type Boolean
 525   * @default true
 526   */
 527  YAHOO.widget.AutoComplete.prototype.autoSnapContainer = true;
 528  
 529  /////////////////////////////////////////////////////////////////////////////
 530  //
 531  // Public methods
 532  //
 533  /////////////////////////////////////////////////////////////////////////////
 534  
 535   /**
 536   * Public accessor to the unique name of the AutoComplete instance.
 537   *
 538   * @method toString
 539   * @return {String} Unique name of the AutoComplete instance.
 540   */
 541  YAHOO.widget.AutoComplete.prototype.toString = function() {
 542      return "AutoComplete " + this._sName;
 543  };
 544  
 545   /**
 546   * Returns DOM reference to input element.
 547   *
 548   * @method getInputEl
 549   * @return {HTMLELement} DOM reference to input element.
 550   */
 551  YAHOO.widget.AutoComplete.prototype.getInputEl = function() {
 552      return this._elTextbox;
 553  };
 554  
 555   /**
 556   * Returns DOM reference to container element.
 557   *
 558   * @method getContainerEl
 559   * @return {HTMLELement} DOM reference to container element.
 560   */
 561  YAHOO.widget.AutoComplete.prototype.getContainerEl = function() {
 562      return this._elContainer;
 563  };
 564  
 565   /**
 566   * Returns true if widget instance is currently active.
 567   *
 568   * @method isFocused
 569   * @return {Boolean} Returns true if widget instance is currently active.
 570   */
 571  YAHOO.widget.AutoComplete.prototype.isFocused = function() {
 572      return this._bFocused;
 573  };
 574  
 575   /**
 576   * Returns true if container is in an expanded state, false otherwise.
 577   *
 578   * @method isContainerOpen
 579   * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
 580   */
 581  YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
 582      return this._bContainerOpen;
 583  };
 584  
 585  /**
 586   * Public accessor to the &lt;ul&gt; element that displays query results within the results container.
 587   *
 588   * @method getListEl
 589   * @return {HTMLElement[]} Reference to &lt;ul&gt; element within the results container.
 590   */
 591  YAHOO.widget.AutoComplete.prototype.getListEl = function() {
 592      return this._elList;
 593  };
 594  
 595  /**
 596   * Public accessor to the matching string associated with a given &lt;li&gt; result.
 597   *
 598   * @method getListItemMatch
 599   * @param elListItem {HTMLElement} Reference to &lt;LI&gt; element.
 600   * @return {String} Matching string.
 601   */
 602  YAHOO.widget.AutoComplete.prototype.getListItemMatch = function(elListItem) {
 603      if(elListItem._sResultMatch) {
 604          return elListItem._sResultMatch;
 605      }
 606      else {
 607          return null;
 608      }
 609  };
 610  
 611  /**
 612   * Public accessor to the result data associated with a given &lt;li&gt; result.
 613   *
 614   * @method getListItemData
 615   * @param elListItem {HTMLElement} Reference to &lt;LI&gt; element.
 616   * @return {Object} Result data.
 617   */
 618  YAHOO.widget.AutoComplete.prototype.getListItemData = function(elListItem) {
 619      if(elListItem._oResultData) {
 620          return elListItem._oResultData;
 621      }
 622      else {
 623          return null;
 624      }
 625  };
 626  
 627  /**
 628   * Public accessor to the index of the associated with a given &lt;li&gt; result.
 629   *
 630   * @method getListItemIndex
 631   * @param elListItem {HTMLElement} Reference to &lt;LI&gt; element.
 632   * @return {Number} Index.
 633   */
 634  YAHOO.widget.AutoComplete.prototype.getListItemIndex = function(elListItem) {
 635      if(YAHOO.lang.isNumber(elListItem._nItemIndex)) {
 636          return elListItem._nItemIndex;
 637      }
 638      else {
 639          return null;
 640      }
 641  };
 642  
 643  /**
 644   * Sets HTML markup for the results container header. This markup will be
 645   * inserted within a &lt;div&gt; tag with a class of "yui-ac-hd".
 646   *
 647   * @method setHeader
 648   * @param sHeader {HTML} HTML markup for results container header.
 649   */
 650  YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
 651      if(this._elHeader) {
 652          var elHeader = this._elHeader;
 653          if(sHeader) {
 654              elHeader.innerHTML = sHeader;
 655              elHeader.style.display = "";
 656          }
 657          else {
 658              elHeader.innerHTML = "";
 659              elHeader.style.display = "none";
 660          }
 661      }
 662  };
 663  
 664  /**
 665   * Sets HTML markup for the results container footer. This markup will be
 666   * inserted within a &lt;div&gt; tag with a class of "yui-ac-ft".
 667   *
 668   * @method setFooter
 669   * @param sFooter {HTML} HTML markup for results container footer.
 670   */
 671  YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
 672      if(this._elFooter) {
 673          var elFooter = this._elFooter;
 674          if(sFooter) {
 675                  elFooter.innerHTML = sFooter;
 676                  elFooter.style.display = "";
 677          }
 678          else {
 679              elFooter.innerHTML = "";
 680              elFooter.style.display = "none";
 681          }
 682      }
 683  };
 684  
 685  /**
 686   * Sets HTML markup for the results container body. This markup will be
 687   * inserted within a &lt;div&gt; tag with a class of "yui-ac-bd".
 688   *
 689   * @method setBody
 690   * @param sBody {HTML} HTML markup for results container body.
 691   */
 692  YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
 693      if(this._elBody) {
 694          var elBody = this._elBody;
 695          YAHOO.util.Event.purgeElement(elBody, true);
 696          if(sBody) {
 697              elBody.innerHTML = sBody;
 698              elBody.style.display = "";
 699          }
 700          else {
 701              elBody.innerHTML = "";
 702              elBody.style.display = "none";
 703          }
 704          this._elList = null;
 705      }
 706  };
 707  
 708  /**
 709  * A function that converts an AutoComplete query into a request value which is then
 710  * passed to the DataSource's sendRequest method in order to retrieve data for 
 711  * the query. By default, returns a String with the syntax: "query={query}"
 712  * Implementers can customize this method for custom request syntaxes.
 713  * 
 714  * @method generateRequest
 715  * @param sQuery {String} Query string
 716  * @return {MIXED} Request
 717  */
 718  YAHOO.widget.AutoComplete.prototype.generateRequest = function(sQuery) {
 719      var dataType = this.dataSource.dataType;
 720      
 721      // Transform query string in to a request for remote data
 722      // By default, local data doesn't need a transformation, just passes along the query as is.
 723      if(dataType === YAHOO.util.DataSourceBase.TYPE_XHR) {
 724          // By default, XHR GET requests look like "{scriptURI}?{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
 725          if(!this.dataSource.connMethodPost) {
 726              sQuery = (this.queryQuestionMark ? "?" : "") + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
 727                  (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");        
 728          }
 729          // By default, XHR POST bodies are sent to the {scriptURI} like "{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
 730          else {
 731              sQuery = (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
 732                  (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");
 733          }
 734      }
 735      // By default, remote script node requests look like "{scriptURI}&{scriptCallbackParam}={callbackString}&{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
 736      else if(dataType === YAHOO.util.DataSourceBase.TYPE_SCRIPTNODE) {
 737          sQuery = "&" + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
 738              (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");    
 739      }
 740      
 741      return sQuery;
 742  };
 743  
 744  /**
 745   * Makes query request to the DataSource.
 746   *
 747   * @method sendQuery
 748   * @param sQuery {String} Query string.
 749   */
 750  YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
 751      // Activate focus for a new interaction
 752      this._bFocused = true;
 753      
 754      // Adjust programatically sent queries to look like they were input by user
 755      // when delimiters are enabled
 756      var newQuery = (this.delimChar) ? this._elTextbox.value + sQuery : sQuery;
 757      this._sendQuery(newQuery);
 758  };
 759  
 760  /**
 761   * Snaps container to bottom-left corner of input element
 762   *
 763   * @method snapContainer
 764   */
 765  YAHOO.widget.AutoComplete.prototype.snapContainer = function() {
 766      var oTextbox = this._elTextbox,
 767          pos = YAHOO.util.Dom.getXY(oTextbox);
 768      pos[1] += YAHOO.util.Dom.get(oTextbox).offsetHeight + 2;
 769      YAHOO.util.Dom.setXY(this._elContainer,pos);
 770  };
 771  
 772  /**
 773   * Expands container.
 774   *
 775   * @method expandContainer
 776   */
 777  YAHOO.widget.AutoComplete.prototype.expandContainer = function() {
 778      this._toggleContainer(true);
 779  };
 780  
 781  /**
 782   * Collapses container.
 783   *
 784   * @method collapseContainer
 785   */
 786  YAHOO.widget.AutoComplete.prototype.collapseContainer = function() {
 787      this._toggleContainer(false);
 788  };
 789  
 790  /**
 791   * Clears entire list of suggestions.
 792   *
 793   * @method clearList
 794   */
 795  YAHOO.widget.AutoComplete.prototype.clearList = function() {
 796      var allItems = this._elList.childNodes,
 797          i=allItems.length-1;
 798      for(; i>-1; i--) {
 799            allItems[i].style.display = "none";
 800      }
 801  };
 802  
 803  /**
 804   * Handles subset matching for when queryMatchSubset is enabled.
 805   *
 806   * @method getSubsetMatches
 807   * @param sQuery {String} Query string.
 808   * @return {Object} oParsedResponse or null. 
 809   */
 810  YAHOO.widget.AutoComplete.prototype.getSubsetMatches = function(sQuery) {
 811      var subQuery, oCachedResponse, subRequest;
 812      // Loop through substrings of each cached element's query property...
 813      for(var i = sQuery.length; i >= this.minQueryLength ; i--) {
 814          subRequest = this.generateRequest(sQuery.substr(0,i));
 815          this.dataRequestEvent.fire(this, subQuery, subRequest);
 816          YAHOO.log("Searching for query subset \"" + subQuery + "\" in cache", "info", this.toString());
 817          
 818          // If a substring of the query is found in the cache
 819          oCachedResponse = this.dataSource.getCachedResponse(subRequest);
 820          if(oCachedResponse) {
 821              YAHOO.log("Found match for query subset \"" + subQuery + "\": " + YAHOO.lang.dump(oCachedResponse), "info", this.toString());
 822              return this.filterResults.apply(this.dataSource, [sQuery, oCachedResponse, oCachedResponse, {scope:this}]);
 823          }
 824      }
 825      YAHOO.log("Did not find subset match for query subset \"" + sQuery + "\"" , "info", this.toString());
 826      return null;
 827  };
 828  
 829  /**
 830   * Executed by DataSource (within DataSource scope via doBeforeParseData()) to
 831   * handle responseStripAfter cleanup.
 832   *
 833   * @method preparseRawResponse
 834   * @param sQuery {String} Query string.
 835   * @return {Object} oParsedResponse or null. 
 836   */
 837  YAHOO.widget.AutoComplete.prototype.preparseRawResponse = function(oRequest, oFullResponse, oCallback) {
 838      var nEnd = ((this.responseStripAfter !== "") && (oFullResponse.indexOf)) ?
 839          oFullResponse.indexOf(this.responseStripAfter) : -1;
 840      if(nEnd != -1) {
 841          oFullResponse = oFullResponse.substring(0,nEnd);
 842      }
 843      return oFullResponse;
 844  };
 845  
 846  /**
 847   * Executed by DataSource (within DataSource scope via doBeforeCallback()) to
 848   * filter results through a simple client-side matching algorithm. 
 849   *
 850   * @method filterResults
 851   * @param sQuery {String} Original request.
 852   * @param oFullResponse {Object} Full response object.
 853   * @param oParsedResponse {Object} Parsed response object.
 854   * @param oCallback {Object} Callback object. 
 855   * @return {Object} Filtered response object.
 856   */
 857  
 858  YAHOO.widget.AutoComplete.prototype.filterResults = function(sQuery, oFullResponse, oParsedResponse, oCallback) {
 859      // If AC has passed a query string value back to itself, grab it
 860      if(oCallback && oCallback.argument && YAHOO.lang.isValue(oCallback.argument.query)) {
 861          sQuery = oCallback.argument.query;
 862      }
 863  
 864      // Only if a query string is available to match against
 865      if(sQuery && sQuery !== "") {
 866          // First make a copy of the oParseResponse
 867          oParsedResponse = YAHOO.widget.AutoComplete._cloneObject(oParsedResponse);
 868          
 869          var oAC = oCallback.scope,
 870              oDS = this,
 871              allResults = oParsedResponse.results, // the array of results
 872              filteredResults = [], // container for filtered results,
 873              nMax = oAC.maxResultsDisplayed, // max to find
 874              bMatchCase = (oDS.queryMatchCase || oAC.queryMatchCase), // backward compat
 875              bMatchContains = (oDS.queryMatchContains || oAC.queryMatchContains); // backward compat
 876              
 877          // Loop through each result object...
 878          for(var i=0, len=allResults.length; i<len; i++) {
 879              var oResult = allResults[i];
 880  
 881              // Grab the data to match against from the result object...
 882              var sResult = null;
 883              
 884              // Result object is a simple string already
 885              if(YAHOO.lang.isString(oResult)) {
 886                  sResult = oResult;
 887              }
 888              // Result object is an array of strings
 889              else if(YAHOO.lang.isArray(oResult)) {
 890                  sResult = oResult[0];
 891              
 892              }
 893              // Result object is an object literal of strings
 894              else if(this.responseSchema.fields) {
 895                  var key = this.responseSchema.fields[0].key || this.responseSchema.fields[0];
 896                  sResult = oResult[key];
 897              }
 898              // Backwards compatibility
 899              else if(this.key) {
 900                  sResult = oResult[this.key];
 901              }
 902              
 903              if(YAHOO.lang.isString(sResult)) {
 904                  
 905                  var sKeyIndex = (bMatchCase) ?
 906                  sResult.indexOf(decodeURIComponent(sQuery)) :
 907                  sResult.toLowerCase().indexOf(decodeURIComponent(sQuery).toLowerCase());
 908  
 909                  // A STARTSWITH match is when the query is found at the beginning of the key string...
 910                  if((!bMatchContains && (sKeyIndex === 0)) ||
 911                  // A CONTAINS match is when the query is found anywhere within the key string...
 912                  (bMatchContains && (sKeyIndex > -1))) {
 913                      // Stash the match
 914                      filteredResults.push(oResult);
 915                  }
 916              }
 917              
 918              // Filter no more if maxResultsDisplayed is reached
 919              if(len>nMax && filteredResults.length===nMax) {
 920                  break;
 921              }
 922          }
 923          oParsedResponse.results = filteredResults;
 924          YAHOO.log("Filtered " + filteredResults.length + " results against query \""  + sQuery + "\": " + YAHOO.lang.dump(filteredResults), "info", this.toString());
 925      }
 926      else {
 927          YAHOO.log("Did not filter results against query", "info", this.toString());
 928      }
 929      
 930      return oParsedResponse;
 931  };
 932  
 933  /**
 934   * Handles response for display. This is the callback function method passed to
 935   * YAHOO.util.DataSourceBase#sendRequest so results from the DataSource are
 936   * returned to the AutoComplete instance.
 937   *
 938   * @method handleResponse
 939   * @param sQuery {String} Original request.
 940   * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
 941   * @param oPayload {MIXED} (optional) Additional argument(s)
 942   */
 943  YAHOO.widget.AutoComplete.prototype.handleResponse = function(sQuery, oResponse, oPayload) {
 944      if((this instanceof YAHOO.widget.AutoComplete) && this._sName) {
 945          this._populateList(sQuery, oResponse, oPayload);
 946      }
 947  };
 948  
 949  /**
 950   * Overridable method called before container is loaded with result data.
 951   *
 952   * @method doBeforeLoadData
 953   * @param sQuery {String} Original request.
 954   * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
 955   * @param oPayload {MIXED} (optional) Additional argument(s)
 956   * @return {Boolean} Return true to continue loading data, false to cancel.
 957   */
 958  YAHOO.widget.AutoComplete.prototype.doBeforeLoadData = function(sQuery, oResponse, oPayload) {
 959      return true;
 960  };
 961  
 962  /**
 963   * Overridable method that returns HTML markup for one result to be populated
 964   * as innerHTML of an &lt;LI&gt; element. 
 965   *
 966   * @method formatResult
 967   * @param oResultData {Object} Result data object.
 968   * @param sQuery {String} The corresponding query string.
 969   * @param sResultMatch {HTMLElement} The current query string. 
 970   * @return {HTML} HTML markup of formatted result data.
 971   */
 972  YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultData, sQuery, sResultMatch) {
 973      var sMarkup = (sResultMatch) ? sResultMatch : "";
 974      return sMarkup;
 975  };
 976  
 977  /**
 978   * An alternative to the formatResult() method, escapes the result data before
 979   * inserting into DOM. Implementers should point to this method when accessing
 980   * data from third-party sources, from user input, or from otherwise
 981   * untrustworthy sources:
 982   * myAutoComplete.formatResult = myAutoComplete.formatEscapedResult;
 983   *
 984   * @method formatEscapedResult
 985   * @param oResultData {Object} Result data object.
 986   * @param sQuery {String} The corresponding query string.
 987   * @param sResultMatch {HTMLElement} The current query string.
 988   * @return {String} Formatted result data.
 989   */
 990  YAHOO.widget.AutoComplete.prototype.formatEscapedResult = function(oResultData, sQuery, sResultMatch) {
 991      var sResult = (sResultMatch) ? sResultMatch : "";
 992      return YAHOO.lang.escapeHTML(sResult);
 993  };
 994  
 995  /**
 996   * Overridable method called before container expands allows implementers to access data
 997   * and DOM elements.
 998   *
 999   * @method doBeforeExpandContainer
1000   * @param elTextbox {HTMLElement} The text input box.
1001   * @param elContainer {HTMLElement} The container element.
1002   * @param sQuery {String} The query string.
1003   * @param aResults {Object[]}  An array of query results.
1004   * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
1005   */
1006  YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(elTextbox, elContainer, sQuery, aResults) {
1007      return true;
1008  };
1009  
1010  
1011  /**
1012   * Nulls out the entire AutoComplete instance and related objects, removes attached
1013   * event listeners, and clears out DOM elements inside the container. After
1014   * calling this method, the instance reference should be expliclitly nulled by
1015   * implementer, as in myAutoComplete = null. Use with caution!
1016   *
1017   * @method destroy
1018   */
1019  YAHOO.widget.AutoComplete.prototype.destroy = function() {
1020      var instanceName = this.toString();
1021      var elInput = this._elTextbox;
1022      var elContainer = this._elContainer;
1023  
1024      // Unhook custom events
1025      this.textboxFocusEvent.unsubscribeAll();
1026      this.textboxKeyEvent.unsubscribeAll();
1027      this.dataRequestEvent.unsubscribeAll();
1028      this.dataReturnEvent.unsubscribeAll();
1029      this.dataErrorEvent.unsubscribeAll();
1030      this.containerPopulateEvent.unsubscribeAll();
1031      this.containerExpandEvent.unsubscribeAll();
1032      this.typeAheadEvent.unsubscribeAll();
1033      this.itemMouseOverEvent.unsubscribeAll();
1034      this.itemMouseOutEvent.unsubscribeAll();
1035      this.itemArrowToEvent.unsubscribeAll();
1036      this.itemArrowFromEvent.unsubscribeAll();
1037      this.itemSelectEvent.unsubscribeAll();
1038      this.unmatchedItemSelectEvent.unsubscribeAll();
1039      this.selectionEnforceEvent.unsubscribeAll();
1040      this.containerCollapseEvent.unsubscribeAll();
1041      this.textboxBlurEvent.unsubscribeAll();
1042      this.textboxChangeEvent.unsubscribeAll();
1043  
1044      // Unhook DOM events
1045      YAHOO.util.Event.purgeElement(elInput, true);
1046      YAHOO.util.Event.purgeElement(elContainer, true);
1047  
1048      // Remove DOM elements
1049      elContainer.innerHTML = "";
1050  
1051      // Null out objects
1052      for(var key in this) {
1053          if(YAHOO.lang.hasOwnProperty(this, key)) {
1054              this[key] = null;
1055          }
1056      }
1057  
1058      YAHOO.log("AutoComplete instance destroyed: " + instanceName);
1059  };
1060  
1061  /////////////////////////////////////////////////////////////////////////////
1062  //
1063  // Public events
1064  //
1065  /////////////////////////////////////////////////////////////////////////////
1066  
1067  /**
1068   * Fired when the input field receives focus.
1069   *
1070   * @event textboxFocusEvent
1071   * @param type {String} Name of the event.
1072   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1073   */
1074  YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
1075  
1076  /**
1077   * Fired when the input field receives key input.
1078   *
1079   * @event textboxKeyEvent
1080   * @param type {String} Name of the event.
1081   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1082   * @param args[1] {Number} The keycode number.
1083   */
1084  YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
1085  
1086  /**
1087   * Fired when the AutoComplete instance makes a request to the DataSource.
1088   * 
1089   * @event dataRequestEvent
1090   * @param type {String} Name of the event.
1091   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1092   * @param args[1] {String} The query string.
1093   * @param args[2] {Object} The request.
1094   */
1095  YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
1096  
1097  /**
1098   * Fired when the AutoComplete request to the DataSource is canceled.
1099   *
1100   * @event dataRequestCancelEvent
1101   * @param type {String} Name of the event.
1102   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1103   * @param args[1] {String} The query string.
1104   */
1105  YAHOO.widget.AutoComplete.prototype.dataRequestCancelEvent = null;
1106  
1107  /**
1108   * Fired when the AutoComplete instance receives query results from the data
1109   * source.
1110   *
1111   * @event dataReturnEvent
1112   * @param type {String} Name of the event.
1113   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1114   * @param args[1] {String} The query string.
1115   * @param args[2] {Object[]} Results array.
1116   */
1117  YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
1118  
1119  /**
1120   * Fired when the AutoComplete instance does not receive query results from the
1121   * DataSource due to an error.
1122   *
1123   * @event dataErrorEvent
1124   * @param type {String} Name of the event.
1125   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1126   * @param args[1] {String} The query string.
1127   * @param args[2] {Object} The response object, if available.
1128   */
1129  YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
1130  
1131  /**
1132   * Fired when the results container is populated.
1133   *
1134   * @event containerPopulateEvent
1135   * @param type {String} Name of the event.
1136   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1137   */
1138  YAHOO.widget.AutoComplete.prototype.containerPopulateEvent = null;
1139  
1140  /**
1141   * Fired when the results container is expanded.
1142   *
1143   * @event containerExpandEvent
1144   * @param type {String} Name of the event.
1145   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1146   */
1147  YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
1148  
1149  /**
1150   * Fired when the input field has been prefilled by the type-ahead
1151   * feature. 
1152   *
1153   * @event typeAheadEvent
1154   * @param type {String} Name of the event.
1155   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1156   * @param args[1] {String} The query string.
1157   * @param args[2] {String} The prefill string.
1158   */
1159  YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
1160  
1161  /**
1162   * Fired when result item has been moused over.
1163   *
1164   * @event itemMouseOverEvent
1165   * @param type {String} Name of the event.
1166   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1167   * @param args[1] {HTMLElement} The &lt;li&gt element item moused to.
1168   */
1169  YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
1170  
1171  /**
1172   * Fired when result item has been moused out.
1173   *
1174   * @event itemMouseOutEvent
1175   * @param type {String} Name of the event.
1176   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1177   * @param args[1] {HTMLElement} The &lt;li&gt; element item moused from.
1178   */
1179  YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
1180  
1181  /**
1182   * Fired when result item has been arrowed to. 
1183   *
1184   * @event itemArrowToEvent
1185   * @param type {String} Name of the event.
1186   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1187   * @param args[1] {HTMLElement} The &lt;li&gt; element item arrowed to.
1188   */
1189  YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
1190  
1191  /**
1192   * Fired when result item has been arrowed away from.
1193   *
1194   * @event itemArrowFromEvent
1195   * @param type {String} Name of the event.
1196   * @param args[0[ {YAHOO.widget.AutoComplete} The AutoComplete instance.
1197   * @param args[1] {HTMLElement} The &lt;li&gt; element item arrowed from.
1198   */
1199  YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
1200  
1201  /**
1202   * Fired when an item is selected via mouse click, ENTER key, or TAB key.
1203   *
1204   * @event itemSelectEvent
1205   * @param type {String} Name of the event.
1206   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1207   * @param args[1] {HTMLElement} The selected &lt;li&gt; element item.
1208   * @param args[2] {Object} The data returned for the item, either as an object,
1209   * or mapped from the schema into an array.
1210   */
1211  YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
1212  
1213  /**
1214   * Fired when a user selection does not match any of the displayed result items.
1215   *
1216   * @event unmatchedItemSelectEvent
1217   * @param type {String} Name of the event.
1218   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1219   * @param args[1] {String} The selected string.
1220   */
1221  YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
1222  
1223  /**
1224   * Fired if forceSelection is enabled and the user's input has been cleared
1225   * because it did not match one of the returned query results.
1226   *
1227   * @event selectionEnforceEvent
1228   * @param type {String} Name of the event.
1229   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1230   * @param args[1] {String} The cleared value (including delimiters if applicable).
1231   */
1232  YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
1233  
1234  /**
1235   * Fired when the results container is collapsed.
1236   *
1237   * @event containerCollapseEvent
1238   * @param type {String} Name of the event.
1239   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1240   */
1241  YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
1242  
1243  /**
1244   * Fired when the input field loses focus.
1245   *
1246   * @event textboxBlurEvent
1247   * @param type {String} Name of the event.
1248   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1249   */
1250  YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
1251  
1252  /**
1253   * Fired when the input field value has changed when it loses focus.
1254   *
1255   * @event textboxChangeEvent
1256   * @param type {String} Name of the event.
1257   * @param args[0] {YAHOO.widget.AutoComplete} The AutoComplete instance.
1258   */
1259  YAHOO.widget.AutoComplete.prototype.textboxChangeEvent = null;
1260  
1261  /////////////////////////////////////////////////////////////////////////////
1262  //
1263  // Private member variables
1264  //
1265  /////////////////////////////////////////////////////////////////////////////
1266  
1267  /**
1268   * Internal class variable to index multiple AutoComplete instances.
1269   *
1270   * @property _nIndex
1271   * @type Number
1272   * @default 0
1273   * @private
1274   */
1275  YAHOO.widget.AutoComplete._nIndex = 0;
1276  
1277  /**
1278   * Name of AutoComplete instance.
1279   *
1280   * @property _sName
1281   * @type String
1282   * @private
1283   */
1284  YAHOO.widget.AutoComplete.prototype._sName = null;
1285  
1286  /**
1287   * Text input field DOM element.
1288   *
1289   * @property _elTextbox
1290   * @type HTMLElement
1291   * @private
1292   */
1293  YAHOO.widget.AutoComplete.prototype._elTextbox = null;
1294  
1295  /**
1296   * Container DOM element.
1297   *
1298   * @property _elContainer
1299   * @type HTMLElement
1300   * @private
1301   */
1302  YAHOO.widget.AutoComplete.prototype._elContainer = null;
1303  
1304  /**
1305   * Reference to content element within container element.
1306   *
1307   * @property _elContent
1308   * @type HTMLElement
1309   * @private
1310   */
1311  YAHOO.widget.AutoComplete.prototype._elContent = null;
1312  
1313  /**
1314   * Reference to header element within content element.
1315   *
1316   * @property _elHeader
1317   * @type HTMLElement
1318   * @private
1319   */
1320  YAHOO.widget.AutoComplete.prototype._elHeader = null;
1321  
1322  /**
1323   * Reference to body element within content element.
1324   *
1325   * @property _elBody
1326   * @type HTMLElement
1327   * @private
1328   */
1329  YAHOO.widget.AutoComplete.prototype._elBody = null;
1330  
1331  /**
1332   * Reference to footer element within content element.
1333   *
1334   * @property _elFooter
1335   * @type HTMLElement
1336   * @private
1337   */
1338  YAHOO.widget.AutoComplete.prototype._elFooter = null;
1339  
1340  /**
1341   * Reference to shadow element within container element.
1342   *
1343   * @property _elShadow
1344   * @type HTMLElement
1345   * @private
1346   */
1347  YAHOO.widget.AutoComplete.prototype._elShadow = null;
1348  
1349  /**
1350   * Reference to iframe element within container element.
1351   *
1352   * @property _elIFrame
1353   * @type HTMLElement
1354   * @private
1355   */
1356  YAHOO.widget.AutoComplete.prototype._elIFrame = null;
1357  
1358  /**
1359   * Whether or not the widget instance is currently active. If query results come back
1360   * but the user has already moved on, do not proceed with auto complete behavior.
1361   *
1362   * @property _bFocused
1363   * @type Boolean
1364   * @private
1365   */
1366  YAHOO.widget.AutoComplete.prototype._bFocused = false;
1367  
1368  /**
1369   * Animation instance for container expand/collapse.
1370   *
1371   * @property _oAnim
1372   * @type Boolean
1373   * @private
1374   */
1375  YAHOO.widget.AutoComplete.prototype._oAnim = null;
1376  
1377  /**
1378   * Whether or not the results container is currently open.
1379   *
1380   * @property _bContainerOpen
1381   * @type Boolean
1382   * @private
1383   */
1384  YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
1385  
1386  /**
1387   * Whether or not the mouse is currently over the results
1388   * container. This is necessary in order to prevent clicks on container items
1389   * from being text input field blur events.
1390   *
1391   * @property _bOverContainer
1392   * @type Boolean
1393   * @private
1394   */
1395  YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
1396  
1397  /**
1398   * Internal reference to &lt;ul&gt; elements that contains query results within the
1399   * results container.
1400   *
1401   * @property _elList
1402   * @type HTMLElement
1403   * @private
1404   */
1405  YAHOO.widget.AutoComplete.prototype._elList = null;
1406  
1407  /*
1408   * Array of &lt;li&gt; elements references that contain query results within the
1409   * results container.
1410   *
1411   * @property _aListItemEls
1412   * @type HTMLElement[]
1413   * @private
1414   */
1415  //YAHOO.widget.AutoComplete.prototype._aListItemEls = null;
1416  
1417  /**
1418   * Number of &lt;li&gt; elements currently displayed in results container.
1419   *
1420   * @property _nDisplayedItems
1421   * @type Number
1422   * @private
1423   */
1424  YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
1425  
1426  /*
1427   * Internal count of &lt;li&gt; elements displayed and hidden in results container.
1428   *
1429   * @property _maxResultsDisplayed
1430   * @type Number
1431   * @private
1432   */
1433  //YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
1434  
1435  /**
1436   * Current query string
1437   *
1438   * @property _sCurQuery
1439   * @type String
1440   * @private
1441   */
1442  YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
1443  
1444  /**
1445   * Selections from previous queries (for saving delimited queries).
1446   *
1447   * @property _sPastSelections
1448   * @type String
1449   * @default "" 
1450   * @private
1451   */
1452  YAHOO.widget.AutoComplete.prototype._sPastSelections = "";
1453  
1454  /**
1455   * Stores initial input value used to determine if textboxChangeEvent should be fired.
1456   *
1457   * @property _sInitInputValue
1458   * @type String
1459   * @private
1460   */
1461  YAHOO.widget.AutoComplete.prototype._sInitInputValue = null;
1462  
1463  /**
1464   * Pointer to the currently highlighted &lt;li&gt; element in the container.
1465   *
1466   * @property _elCurListItem
1467   * @type HTMLElement
1468   * @private
1469   */
1470  YAHOO.widget.AutoComplete.prototype._elCurListItem = null;
1471  
1472  /**
1473   * Pointer to the currently pre-highlighted &lt;li&gt; element in the container.
1474   *
1475   * @property _elCurPrehighlightItem
1476   * @type HTMLElement
1477   * @private
1478   */
1479  YAHOO.widget.AutoComplete.prototype._elCurPrehighlightItem = null;
1480  
1481  /**
1482   * Whether or not an item has been selected since the container was populated
1483   * with results. Reset to false by _populateList, and set to true when item is
1484   * selected.
1485   *
1486   * @property _bItemSelected
1487   * @type Boolean
1488   * @private
1489   */
1490  YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
1491  
1492  /**
1493   * Key code of the last key pressed in textbox.
1494   *
1495   * @property _nKeyCode
1496   * @type Number
1497   * @private
1498   */
1499  YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
1500  
1501  /**
1502   * Delay timeout ID.
1503   *
1504   * @property _nDelayID
1505   * @type Number
1506   * @private
1507   */
1508  YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
1509  
1510  /**
1511   * TypeAhead delay timeout ID.
1512   *
1513   * @property _nTypeAheadDelayID
1514   * @type Number
1515   * @private
1516   */
1517  YAHOO.widget.AutoComplete.prototype._nTypeAheadDelayID = -1;
1518  
1519  /**
1520   * Src to iFrame used when useIFrame = true. Supports implementations over SSL
1521   * as well.
1522   *
1523   * @property _iFrameSrc
1524   * @type String
1525   * @private
1526   */
1527  YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
1528  
1529  /**
1530   * For users typing via certain IMEs, queries must be triggered by intervals,
1531   * since key events yet supported across all browsers for all IMEs.
1532   *
1533   * @property _queryInterval
1534   * @type Object
1535   * @private
1536   */
1537  YAHOO.widget.AutoComplete.prototype._queryInterval = null;
1538  
1539  /**
1540   * Internal tracker to last known textbox value, used to determine whether or not
1541   * to trigger a query via interval for certain IME users.
1542   *
1543   * @event _sLastTextboxValue
1544   * @type String
1545   * @private
1546   */
1547  YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
1548  
1549  /////////////////////////////////////////////////////////////////////////////
1550  //
1551  // Private methods
1552  //
1553  /////////////////////////////////////////////////////////////////////////////
1554  
1555  /**
1556   * Updates and validates latest public config properties.
1557   *
1558   * @method __initProps
1559   * @private
1560   */
1561  YAHOO.widget.AutoComplete.prototype._initProps = function() {
1562      // Correct any invalid values
1563      var minQueryLength = this.minQueryLength;
1564      if(!YAHOO.lang.isNumber(minQueryLength)) {
1565          this.minQueryLength = 1;
1566      }
1567      var maxResultsDisplayed = this.maxResultsDisplayed;
1568      if(!YAHOO.lang.isNumber(maxResultsDisplayed) || (maxResultsDisplayed < 1)) {
1569          this.maxResultsDisplayed = 10;
1570      }
1571      var queryDelay = this.queryDelay;
1572      if(!YAHOO.lang.isNumber(queryDelay) || (queryDelay < 0)) {
1573          this.queryDelay = 0.2;
1574      }
1575      var typeAheadDelay = this.typeAheadDelay;
1576      if(!YAHOO.lang.isNumber(typeAheadDelay) || (typeAheadDelay < 0)) {
1577          this.typeAheadDelay = 0.2;
1578      }
1579      var delimChar = this.delimChar;
1580      if(YAHOO.lang.isString(delimChar) && (delimChar.length > 0)) {
1581          this.delimChar = [delimChar];
1582      }
1583      else if(!YAHOO.lang.isArray(delimChar)) {
1584          this.delimChar = null;
1585      }
1586      var animSpeed = this.animSpeed;
1587      if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
1588          if(!YAHOO.lang.isNumber(animSpeed) || (animSpeed < 0)) {
1589              this.animSpeed = 0.3;
1590          }
1591          if(!this._oAnim ) {
1592              this._oAnim = new YAHOO.util.Anim(this._elContent, {}, this.animSpeed);
1593          }
1594          else {
1595              this._oAnim.duration = this.animSpeed;
1596          }
1597      }
1598      if(this.forceSelection && delimChar) {
1599          YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString());
1600      }
1601  };
1602  
1603  /**
1604   * Initializes the results container helpers if they are enabled and do
1605   * not exist
1606   *
1607   * @method _initContainerHelperEls
1608   * @private
1609   */
1610  YAHOO.widget.AutoComplete.prototype._initContainerHelperEls = function() {
1611      if(this.useShadow && !this._elShadow) {
1612          var elShadow = document.createElement("div");
1613          elShadow.className = "yui-ac-shadow";
1614          elShadow.style.width = 0;
1615          elShadow.style.height = 0;
1616          this._elShadow = this._elContainer.appendChild(elShadow);
1617      }
1618      if(this.useIFrame && !this._elIFrame) {
1619          var elIFrame = document.createElement("iframe");
1620          elIFrame.src = this._iFrameSrc;
1621          elIFrame.frameBorder = 0;
1622          elIFrame.scrolling = "no";
1623          elIFrame.style.position = "absolute";
1624          elIFrame.style.width = 0;
1625          elIFrame.style.height = 0;
1626          elIFrame.style.padding = 0;
1627          elIFrame.tabIndex = -1;
1628          elIFrame.role = "presentation";
1629          elIFrame.title = "Presentational iframe shim";
1630          this._elIFrame = this._elContainer.appendChild(elIFrame);
1631      }
1632  };
1633  
1634  /**
1635   * Initializes the results container once at object creation
1636   *
1637   * @method _initContainerEl
1638   * @private
1639   */
1640  YAHOO.widget.AutoComplete.prototype._initContainerEl = function() {
1641      YAHOO.util.Dom.addClass(this._elContainer, "yui-ac-container");
1642      
1643      if(!this._elContent) {
1644          // The elContent div is assigned DOM listeners and 
1645          // helps size the iframe and shadow properly
1646          var elContent = document.createElement("div");
1647          elContent.className = "yui-ac-content";
1648          elContent.style.display = "none";
1649  
1650          this._elContent = this._elContainer.appendChild(elContent);
1651  
1652          var elHeader = document.createElement("div");
1653          elHeader.className = "yui-ac-hd";
1654          elHeader.style.display = "none";
1655          this._elHeader = this._elContent.appendChild(elHeader);
1656  
1657          var elBody = document.createElement("div");
1658          elBody.className = "yui-ac-bd";
1659          this._elBody = this._elContent.appendChild(elBody);
1660  
1661          var elFooter = document.createElement("div");
1662          elFooter.className = "yui-ac-ft";
1663          elFooter.style.display = "none";
1664          this._elFooter = this._elContent.appendChild(elFooter);
1665      }
1666      else {
1667          YAHOO.log("Could not initialize the container","warn",this.toString());
1668      }
1669  };
1670  
1671  /**
1672   * Clears out contents of container body and creates up to
1673   * YAHOO.widget.AutoComplete#maxResultsDisplayed &lt;li&gt; elements in an
1674   * &lt;ul&gt; element.
1675   *
1676   * @method _initListEl
1677   * @private
1678   */
1679  YAHOO.widget.AutoComplete.prototype._initListEl = function() {
1680      var nListLength = this.maxResultsDisplayed,
1681          elList = this._elList || document.createElement("ul"),
1682          elListItem;
1683      
1684      while(elList.childNodes.length < nListLength) {
1685          elListItem = document.createElement("li");
1686          elListItem.style.display = "none";
1687          elListItem._nItemIndex = elList.childNodes.length;
1688          elList.appendChild(elListItem);
1689      }
1690      if(!this._elList) {
1691          var elBody = this._elBody;
1692          YAHOO.util.Event.purgeElement(elBody, true);
1693          elBody.innerHTML = "";
1694          this._elList = elBody.appendChild(elList);
1695      }
1696      
1697      this._elBody.style.display = "";
1698  };
1699  
1700  /**
1701   * Focuses input field.
1702   *
1703   * @method _focus
1704   * @private
1705   */
1706  YAHOO.widget.AutoComplete.prototype._focus = function() {
1707      // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
1708      var oSelf = this;
1709      setTimeout(function() {
1710          try {
1711              oSelf._elTextbox.focus();
1712          }
1713          catch(e) {
1714          }
1715      },0);
1716  };
1717  
1718  /**
1719   * Enables interval detection for IME support.
1720   *
1721   * @method _enableIntervalDetection
1722   * @private
1723   */
1724  YAHOO.widget.AutoComplete.prototype._enableIntervalDetection = function() {
1725      var oSelf = this;
1726      if(!oSelf._queryInterval && oSelf.queryInterval) {
1727          oSelf._queryInterval = setInterval(function() { oSelf._onInterval(); }, oSelf.queryInterval);
1728          YAHOO.log("Interval set", "info", this.toString());
1729      }
1730  };
1731  
1732  /**
1733   * Enables interval detection for a less performant but brute force mechanism to
1734   * detect input values at an interval set by queryInterval and send queries if
1735   * input value has changed. Needed to support right-click+paste or shift+insert
1736   * edge cases. Please note that intervals are cleared at the end of each interaction,
1737   * so enableIntervalDetection must be called for each new interaction. The
1738   * recommended approach is to call it in response to textboxFocusEvent.
1739   *
1740   * @method enableIntervalDetection
1741   */
1742  YAHOO.widget.AutoComplete.prototype.enableIntervalDetection =
1743      YAHOO.widget.AutoComplete.prototype._enableIntervalDetection;
1744  
1745  /**
1746   * Enables query triggers based on text input detection by intervals (rather
1747   * than by key events).
1748   *
1749   * @method _onInterval
1750   * @private
1751   */
1752  YAHOO.widget.AutoComplete.prototype._onInterval = function() {
1753      var currValue = this._elTextbox.value;
1754      var lastValue = this._sLastTextboxValue;
1755      if(currValue != lastValue) {
1756          this._sLastTextboxValue = currValue;
1757          this._sendQuery(currValue);
1758      }
1759  };
1760  
1761  /**
1762   * Cancels text input detection by intervals.
1763   *
1764   * @method _clearInterval
1765   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1766   * @private
1767   */
1768  YAHOO.widget.AutoComplete.prototype._clearInterval = function() {
1769      if(this._queryInterval) {
1770          clearInterval(this._queryInterval);
1771          this._queryInterval = null;
1772          YAHOO.log("Interval cleared", "info", this.toString());
1773      }
1774  };
1775  
1776  /**
1777   * Whether or not key is functional or should be ignored. Note that the right
1778   * arrow key is NOT an ignored key since it triggers queries for certain intl
1779   * charsets.
1780   *
1781   * @method _isIgnoreKey
1782   * @param nKeycode {Number} Code of key pressed.
1783   * @return {Boolean} True if key should be ignored, false otherwise.
1784   * @private
1785   */
1786  YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) {
1787      if((nKeyCode == 9) || (nKeyCode == 13)  || // tab, enter
1788              (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl
1789              (nKeyCode >= 18 && nKeyCode <= 20) || // alt, pause/break,caps lock
1790              (nKeyCode == 27) || // esc
1791              (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end
1792              /*(nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up
1793              (nKeyCode == 40) || // down*/
1794              (nKeyCode >= 36 && nKeyCode <= 40) || // home,left,up, right, down
1795              (nKeyCode >= 44 && nKeyCode <= 45) || // print screen,insert
1796              (nKeyCode == 229) // Bug 2041973: Korean XP fires 2 keyup events, the key and 229
1797          ) { 
1798          return true;
1799      }
1800      return false;
1801  };
1802  
1803  /**
1804   * Makes query request to the DataSource.
1805   *
1806   * @method _sendQuery
1807   * @param sQuery {String} Query string.
1808   * @private
1809   */
1810  YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) {
1811      // Widget has been effectively turned off
1812      if(this.minQueryLength < 0) {
1813          this._toggleContainer(false);
1814          YAHOO.log("Property minQueryLength is less than 0", "info", this.toString());
1815          return;
1816      }
1817      // Delimiter has been enabled
1818      if(this.delimChar) {
1819          var extraction = this._extractQuery(sQuery);
1820          // Here is the query itself
1821          sQuery = extraction.query;
1822          // ...and save the rest of the string for later
1823          this._sPastSelections = extraction.previous;
1824      }
1825  
1826      // Don't search queries that are too short
1827      if((sQuery && (sQuery.length < this.minQueryLength)) || (!sQuery && this.minQueryLength > 0)) {
1828          if(this._nDelayID != -1) {
1829              clearTimeout(this._nDelayID);
1830          }
1831          this._toggleContainer(false);
1832          YAHOO.log("Query \"" + sQuery + "\" is too short", "info", this.toString());
1833          return;
1834      }
1835  
1836      sQuery = encodeURIComponent(sQuery);
1837      this._nDelayID = -1;    // Reset timeout ID because request is being made
1838      
1839      // Subset matching
1840      if(this.dataSource.queryMatchSubset || this.queryMatchSubset) { // backward compat
1841          var oResponse = this.getSubsetMatches(sQuery);
1842          if(oResponse) {
1843              this.handleResponse(sQuery, oResponse, {query: sQuery});
1844              return;
1845          }
1846      }
1847      
1848      if(this.dataSource.responseStripAfter) {
1849          this.dataSource.doBeforeParseData = this.preparseRawResponse;
1850      }
1851      if(this.applyLocalFilter) {
1852          this.dataSource.doBeforeCallback = this.filterResults;
1853      }
1854      
1855      var sRequest = this.generateRequest(sQuery);
1856      
1857      if(sRequest !== undefined) {
1858          this.dataRequestEvent.fire(this, sQuery, sRequest);
1859          YAHOO.log("Sending query \"" + sRequest + "\"", "info", this.toString());
1860  
1861          this.dataSource.sendRequest(sRequest, {
1862                  success : this.handleResponse,
1863                  failure : this.handleResponse,
1864                  scope   : this,
1865                  argument: {
1866                      query: sQuery
1867                  }
1868          });
1869      }
1870      else {
1871          this.dataRequestCancelEvent.fire(this, sQuery);
1872          YAHOO.log("Canceled query \"" + sQuery + "\"", "info", this.toString());
1873      }
1874  };
1875  
1876  /**
1877   * Populates the given &lt;li&gt; element with return value from formatResult().
1878   *
1879   * @method _populateListItem
1880   * @param elListItem {HTMLElement} The LI element.
1881   * @param oResult {Object} The result object.
1882   * @param sCurQuery {String} The query string.
1883   * @private
1884   */
1885  YAHOO.widget.AutoComplete.prototype._populateListItem = function(elListItem, oResult, sQuery) {
1886      elListItem.innerHTML = this.formatResult(oResult, sQuery, elListItem._sResultMatch);
1887  };
1888  
1889  /**
1890   * Populates the array of &lt;li&gt; elements in the container with query
1891   * results.
1892   *
1893   * @method _populateList
1894   * @param sQuery {String} Original request.
1895   * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
1896   * @param oPayload {MIXED} (optional) Additional argument(s)
1897   * @private
1898   */
1899  YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, oResponse, oPayload) {
1900      // Clear previous timeout
1901      if(this._nTypeAheadDelayID != -1) {
1902          clearTimeout(this._nTypeAheadDelayID);
1903      }
1904          
1905      sQuery = (oPayload && oPayload.query) ? oPayload.query : sQuery;
1906      
1907      // Pass data through abstract method for any transformations
1908      var ok = this.doBeforeLoadData(sQuery, oResponse, oPayload);
1909  
1910      // Data is ok
1911      if(ok && !oResponse.error) {
1912          this.dataReturnEvent.fire(this, sQuery, oResponse.results);
1913          
1914          // Continue only if instance is still active (i.e., user hasn't already moved on)
1915          if(this._bFocused) {
1916              // Store state for this interaction
1917              var sCurQuery = decodeURIComponent(sQuery);
1918              this._sCurQuery = sCurQuery;
1919              this._bItemSelected = false;
1920          
1921              var allResults = oResponse.results,
1922                  nItemsToShow = Math.min(allResults.length,this.maxResultsDisplayed),
1923                  sMatchKey = (this.dataSource.responseSchema.fields) ? 
1924                      (this.dataSource.responseSchema.fields[0].key || this.dataSource.responseSchema.fields[0]) : 0;
1925              
1926              if(nItemsToShow > 0) {
1927                  // Make sure container and helpers are ready to go
1928                  if(!this._elList || (this._elList.childNodes.length < nItemsToShow)) {
1929                      this._initListEl();
1930                  }
1931                  this._initContainerHelperEls();
1932                  
1933                  var allListItemEls = this._elList.childNodes;
1934                  // Fill items with data from the bottom up
1935                  for(var i = nItemsToShow-1; i >= 0; i--) {
1936                      var elListItem = allListItemEls[i],
1937                      oResult = allResults[i];
1938                      
1939                      // Backward compatibility
1940                      if(this.resultTypeList) {
1941                          // Results need to be converted back to an array
1942                          var aResult = [];
1943                          // Match key is first
1944                          aResult[0] = (YAHOO.lang.isString(oResult)) ? oResult : oResult[sMatchKey] || oResult[this.key];
1945                          // Add additional data to the result array
1946                          var fields = this.dataSource.responseSchema.fields;
1947                          if(YAHOO.lang.isArray(fields) && (fields.length > 1)) {
1948                              for(var k=1, len=fields.length; k<len; k++) {
1949                                  aResult[aResult.length] = oResult[fields[k].key || fields[k]];
1950                              }
1951                          }
1952                          // No specific fields defined, so pass along entire data object
1953                          else {
1954                              // Already an array
1955                              if(YAHOO.lang.isArray(oResult)) {
1956                                  aResult = oResult;
1957                              }
1958                              // Simple string 
1959                              else if(YAHOO.lang.isString(oResult)) {
1960                                  aResult = [oResult];
1961                              }
1962                              // Object
1963                              else {
1964                                  aResult[1] = oResult;
1965                              }
1966                          }
1967                          oResult = aResult;
1968                      }
1969  
1970                      // The matching value, including backward compatibility for array format and safety net
1971                      elListItem._sResultMatch = (YAHOO.lang.isString(oResult)) ? oResult : (YAHOO.lang.isArray(oResult)) ? oResult[0] : (oResult[sMatchKey] || "");
1972                      elListItem._oResultData = oResult; // Additional data
1973                      this._populateListItem(elListItem, oResult, sCurQuery);
1974                      elListItem.style.display = "";
1975                  }
1976          
1977                  // Clear out extraneous items
1978                  if(nItemsToShow < allListItemEls.length) {
1979                      var extraListItem;
1980                      for(var j = allListItemEls.length-1; j >= nItemsToShow; j--) {
1981                          extraListItem = allListItemEls[j];
1982                          extraListItem.style.display = "none";
1983                      }
1984                  }
1985                  
1986                  this._nDisplayedItems = nItemsToShow;
1987                  
1988                  this.containerPopulateEvent.fire(this, sQuery, allResults);
1989                  
1990                  // Highlight the first item
1991                  if(this.autoHighlight) {
1992                      var elFirstListItem = this._elList.firstChild;
1993                      this._toggleHighlight(elFirstListItem,"to");
1994                      this.itemArrowToEvent.fire(this, elFirstListItem);
1995                      YAHOO.log("Arrowed to first item", "info", this.toString());
1996                      this._typeAhead(elFirstListItem,sQuery);
1997                  }
1998                  // Unhighlight any previous time
1999                  else {
2000                      this._toggleHighlight(this._elCurListItem,"from");
2001                  }
2002          
2003                  // Pre-expansion stuff
2004                  ok = this._doBeforeExpandContainer(this._elTextbox, this._elContainer, sQuery, allResults);
2005                  
2006                  // Expand the container
2007                  this._toggleContainer(ok);
2008              }
2009              else {
2010                  this._toggleContainer(false);
2011              }
2012  
2013              YAHOO.log("Container populated with " + nItemsToShow +  " list items", "info", this.toString());
2014              return;
2015          }
2016      }
2017      // Error
2018      else {
2019          this.dataErrorEvent.fire(this, sQuery, oResponse);
2020      }
2021          
2022      YAHOO.log("Could not populate list", "info", this.toString());    
2023  };
2024  
2025  /**
2026   * Called before container expands, by default snaps container to the
2027   * bottom-left corner of the input element, then calls public overrideable method.
2028   *
2029   * @method _doBeforeExpandContainer
2030   * @param elTextbox {HTMLElement} The text input box.
2031   * @param elContainer {HTMLElement} The container element.
2032   * @param sQuery {String} The query string.
2033   * @param aResults {Object[]}  An array of query results.
2034   * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
2035   * @private 
2036   */
2037  YAHOO.widget.AutoComplete.prototype._doBeforeExpandContainer = function(elTextbox, elContainer, sQuery, aResults) {
2038      if(this.autoSnapContainer) {
2039          this.snapContainer();
2040      }
2041  
2042      return this.doBeforeExpandContainer(elTextbox, elContainer, sQuery, aResults);
2043  };
2044  
2045  /**
2046   * When forceSelection is true and the user attempts
2047   * leave the text input box without selecting an item from the query results,
2048   * the user selection is cleared.
2049   *
2050   * @method _clearSelection
2051   * @private
2052   */
2053  YAHOO.widget.AutoComplete.prototype._clearSelection = function() {
2054      var extraction = (this.delimChar) ? this._extractQuery(this._elTextbox.value) :
2055              {previous:"",query:this._elTextbox.value};
2056      this._elTextbox.value = extraction.previous;
2057      this.selectionEnforceEvent.fire(this, extraction.query);
2058      YAHOO.log("Selection enforced", "info", this.toString());
2059  };
2060  
2061  /**
2062   * Whether or not user-typed value in the text input box matches any of the
2063   * query results.
2064   *
2065   * @method _textMatchesOption
2066   * @return {HTMLElement} Matching list item element if user-input text matches
2067   * a result, null otherwise.
2068   * @private
2069   */
2070  YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() {
2071      var elMatch = null;
2072  
2073      for(var i=0; i<this._nDisplayedItems; i++) {
2074          var elListItem = this._elList.childNodes[i];
2075          var sMatch = ("" + elListItem._sResultMatch).toLowerCase();
2076          if(sMatch == this._sCurQuery.toLowerCase()) {
2077              elMatch = elListItem;
2078              break;
2079          }
2080      }
2081      return(elMatch);
2082  };
2083  
2084  /**
2085   * Updates in the text input box with the first query result as the user types,
2086   * selecting the substring that the user has not typed.
2087   *
2088   * @method _typeAhead
2089   * @param elListItem {HTMLElement} The &lt;li&gt; element item whose data populates the input field.
2090   * @param sQuery {String} Query string.
2091   * @private
2092   */
2093  YAHOO.widget.AutoComplete.prototype._typeAhead = function(elListItem, sQuery) {
2094      // Don't typeAhead if turned off or is backspace
2095      if(!this.typeAhead || (this._nKeyCode == 8)) {
2096          return;
2097      }
2098  
2099      var oSelf = this,
2100          elTextbox = this._elTextbox;
2101          
2102      // Only if text selection is supported
2103      if(elTextbox.setSelectionRange || elTextbox.createTextRange) {
2104          // Set and store timeout for this typeahead
2105          this._nTypeAheadDelayID = setTimeout(function() {
2106                  // Select the portion of text that the user has not typed
2107                  var nStart = elTextbox.value.length; // any saved queries plus what user has typed
2108                  oSelf._updateValue(elListItem);
2109                  var nEnd = elTextbox.value.length;
2110                  oSelf._selectText(elTextbox,nStart,nEnd);
2111                  var sPrefill = elTextbox.value.substr(nStart,nEnd);
2112                  // Bug 2528552: Store as a selection
2113                  oSelf._sCurQuery = elListItem._sResultMatch;
2114                  oSelf.typeAheadEvent.fire(oSelf,sQuery,sPrefill);
2115                  YAHOO.log("Typeahead occured with prefill string \"" + sPrefill + "\"", "info", oSelf.toString());
2116              },(this.typeAheadDelay*1000));            
2117      }
2118  };
2119  
2120  /**
2121   * Selects text in the input field.
2122   *
2123   * @method _selectText
2124   * @param elTextbox {HTMLElement} Text input box element in which to select text.
2125   * @param nStart {Number} Starting index of text string to select.
2126   * @param nEnd {Number} Ending index of text selection.
2127   * @private
2128   */
2129  YAHOO.widget.AutoComplete.prototype._selectText = function(elTextbox, nStart, nEnd) {
2130      if(elTextbox.setSelectionRange) { // For Mozilla
2131          elTextbox.setSelectionRange(nStart,nEnd);
2132      }
2133      else if(elTextbox.createTextRange) { // For IE
2134          var oTextRange = elTextbox.createTextRange();
2135          oTextRange.moveStart("character", nStart);
2136          oTextRange.moveEnd("character", nEnd-elTextbox.value.length);
2137          oTextRange.select();
2138      }
2139      else {
2140          elTextbox.select();
2141      }
2142  };
2143  
2144  /**
2145   * Extracts rightmost query from delimited string.
2146   *
2147   * @method _extractQuery
2148   * @param sQuery {String} String to parse
2149   * @return {Object} Object literal containing properties "query" and "previous".  
2150   * @private
2151   */
2152  YAHOO.widget.AutoComplete.prototype._extractQuery = function(sQuery) {
2153      var aDelimChar = this.delimChar,
2154          nDelimIndex = -1,
2155          nNewIndex, nQueryStart,
2156          i = aDelimChar.length-1,
2157          sPrevious;
2158          
2159      // Loop through all possible delimiters and find the rightmost one in the query
2160      // A " " may be a false positive if they are defined as delimiters AND
2161      // are used to separate delimited queries
2162      for(; i >= 0; i--) {
2163          nNewIndex = sQuery.lastIndexOf(aDelimChar[i]);
2164          if(nNewIndex > nDelimIndex) {
2165              nDelimIndex = nNewIndex;
2166          }
2167      }
2168      // If we think the last delimiter is a space (" "), make sure it is NOT
2169      // a false positive by also checking the char directly before it
2170      if(aDelimChar[i] == " ") {
2171          for (var j = aDelimChar.length-1; j >= 0; j--) {
2172              if(sQuery[nDelimIndex - 1] == aDelimChar[j]) {
2173                  nDelimIndex--;
2174                  break;
2175              }
2176          }
2177      }
2178      // A delimiter has been found in the query so extract the latest query from past selections
2179      if(nDelimIndex > -1) {
2180          nQueryStart = nDelimIndex + 1;
2181          // Trim any white space from the beginning...
2182          while(sQuery.charAt(nQueryStart) == " ") {
2183              nQueryStart += 1;
2184          }
2185          // ...and save the rest of the string for later
2186          sPrevious = sQuery.substring(0,nQueryStart);
2187          // Here is the query itself
2188          sQuery = sQuery.substr(nQueryStart);
2189      }
2190      // No delimiter found in the query, so there are no selections from past queries
2191      else {
2192          sPrevious = "";
2193      }
2194      
2195      return {
2196          previous: sPrevious,
2197          query: sQuery
2198      };
2199  };
2200  
2201  /**
2202   * Syncs results container with its helpers.
2203   *
2204   * @method _toggleContainerHelpers
2205   * @param bShow {Boolean} True if container is expanded, false if collapsed
2206   * @private
2207   */
2208  YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) {
2209      var width = this._elContent.offsetWidth + "px";
2210      var height = this._elContent.offsetHeight + "px";
2211  
2212      if(this.useIFrame && this._elIFrame) {
2213      var elIFrame = this._elIFrame;
2214          if(bShow) {
2215              elIFrame.style.width = width;
2216              elIFrame.style.height = height;
2217              elIFrame.style.padding = "";
2218              YAHOO.log("Iframe expanded", "info", this.toString());
2219          }
2220          else {
2221              elIFrame.style.width = 0;
2222              elIFrame.style.height = 0;
2223              elIFrame.style.padding = 0;
2224              YAHOO.log("Iframe collapsed", "info", this.toString());
2225          }
2226      }
2227      if(this.useShadow && this._elShadow) {
2228      var elShadow = this._elShadow;
2229          if(bShow) {
2230              elShadow.style.width = width;
2231              elShadow.style.height = height;
2232              YAHOO.log("Shadow expanded", "info", this.toString());
2233          }
2234          else {
2235              elShadow.style.width = 0;
2236              elShadow.style.height = 0;
2237              YAHOO.log("Shadow collapsed", "info", this.toString());
2238          }
2239      }
2240  };
2241  
2242  /**
2243   * Animates expansion or collapse of the container.
2244   *
2245   * @method _toggleContainer
2246   * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed
2247   * @private
2248   */
2249  YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) {
2250      YAHOO.log("Toggling container " + ((bShow) ? "open" : "closed"), "info", this.toString());
2251  
2252      var elContainer = this._elContainer;
2253  
2254      // If implementer has container always open and it's already open, don't mess with it
2255      // Container is initialized with display "none" so it may need to be shown first time through
2256      if(this.alwaysShowContainer && this._bContainerOpen) {
2257          return;
2258      }
2259      
2260      // Reset states
2261      if(!bShow) {
2262          this._toggleHighlight(this._elCurListItem,"from");
2263          this._nDisplayedItems = 0;
2264          this._sCurQuery = null;
2265          
2266          // Container is already closed, so don't bother with changing the UI
2267          if(this._elContent.style.display == "none") {
2268              return;
2269          }
2270      }
2271  
2272      // If animation is enabled...
2273      var oAnim = this._oAnim;
2274      if(oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) {
2275          if(oAnim.isAnimated()) {
2276              oAnim.stop(true);
2277          }
2278  
2279          // Clone container to grab current size offscreen
2280          var oClone = this._elContent.cloneNode(true);
2281          elContainer.appendChild(oClone);
2282          oClone.style.top = "-9000px";
2283          oClone.style.width = "";
2284          oClone.style.height = "";
2285          oClone.style.display = "";
2286  
2287          // Current size of the container is the EXPANDED size
2288          var wExp = oClone.offsetWidth;
2289          var hExp = oClone.offsetHeight;
2290  
2291          // Calculate COLLAPSED sizes based on horiz and vert anim
2292          var wColl = (this.animHoriz) ? 0 : wExp;
2293          var hColl = (this.animVert) ? 0 : hExp;
2294  
2295          // Set animation sizes
2296          oAnim.attributes = (bShow) ?
2297              {width: { to: wExp }, height: { to: hExp }} :
2298              {width: { to: wColl}, height: { to: hColl }};
2299  
2300          // If opening anew, set to a collapsed size...
2301          if(bShow && !this._bContainerOpen) {
2302              this._elContent.style.width = wColl+"px";
2303              this._elContent.style.height = hColl+"px";
2304          }
2305          // Else, set it to its last known size.
2306          else {
2307              this._elContent.style.width = wExp+"px";
2308              this._elContent.style.height = hExp+"px";
2309          }
2310  
2311          elContainer.removeChild(oClone);
2312          oClone = null;
2313  
2314          var oSelf = this;
2315          var onAnimComplete = function() {
2316              // Finish the collapse
2317              oAnim.onComplete.unsubscribeAll();
2318  
2319              if(bShow) {
2320                  oSelf._toggleContainerHelpers(true);
2321                  oSelf._bContainerOpen = bShow;
2322                  oSelf.containerExpandEvent.fire(oSelf);
2323                  YAHOO.log("Container expanded", "info", oSelf.toString());
2324              }
2325              else {
2326                  oSelf._elContent.style.display = "none";
2327                  oSelf._bContainerOpen = bShow;
2328                  oSelf.containerCollapseEvent.fire(oSelf);
2329                  YAHOO.log("Container collapsed", "info", oSelf.toString());
2330              }
2331           };
2332  
2333          // Display container and animate it
2334          this._toggleContainerHelpers(false); // Bug 1424486: Be early to hide, late to show;
2335          this._elContent.style.display = "";
2336          oAnim.onComplete.subscribe(onAnimComplete);
2337          oAnim.animate();
2338      }
2339      // Else don't animate, just show or hide
2340      else {
2341          if(bShow) {
2342              this._elContent.style.display = "";
2343              this._toggleContainerHelpers(true);
2344              this._bContainerOpen = bShow;
2345              this.containerExpandEvent.fire(this);
2346              YAHOO.log("Container expanded", "info", this.toString());
2347          }
2348          else {
2349              this._toggleContainerHelpers(false);
2350              this._elContent.style.display = "none";
2351              this._bContainerOpen = bShow;
2352              this.containerCollapseEvent.fire(this);
2353              YAHOO.log("Container collapsed", "info", this.toString());
2354          }
2355     }
2356  
2357  };
2358  
2359  /**
2360   * Toggles the highlight on or off for an item in the container, and also cleans
2361   * up highlighting of any previous item.
2362   *
2363   * @method _toggleHighlight
2364   * @param elNewListItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
2365   * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
2366   * @private
2367   */
2368  YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(elNewListItem, sType) {
2369      if(elNewListItem) {
2370          var sHighlight = this.highlightClassName;
2371          if(this._elCurListItem) {
2372              // Remove highlight from old item
2373              YAHOO.util.Dom.removeClass(this._elCurListItem, sHighlight);
2374              this._elCurListItem = null;
2375          }
2376      
2377          if((sType == "to") && sHighlight) {
2378              // Apply highlight to new item
2379              YAHOO.util.Dom.addClass(elNewListItem, sHighlight);
2380              this._elCurListItem = elNewListItem;
2381          }
2382      }
2383  };
2384  
2385  /**
2386   * Toggles the pre-highlight on or off for an item in the container, and also cleans
2387   * up pre-highlighting of any previous item.
2388   *
2389   * @method _togglePrehighlight
2390   * @param elNewListItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
2391   * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
2392   * @private
2393   */
2394  YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(elNewListItem, sType) {
2395      var sPrehighlight = this.prehighlightClassName;
2396  
2397      if(this._elCurPrehighlightItem) {
2398          YAHOO.util.Dom.removeClass(this._elCurPrehighlightItem, sPrehighlight);
2399      }
2400      if(elNewListItem == this._elCurListItem) {
2401          return;
2402      }
2403  
2404      if((sType == "mouseover") && sPrehighlight) {
2405          // Apply prehighlight to new item
2406          YAHOO.util.Dom.addClass(elNewListItem, sPrehighlight);
2407          this._elCurPrehighlightItem = elNewListItem;
2408      }
2409      else {
2410          // Remove prehighlight from old item
2411          YAHOO.util.Dom.removeClass(elNewListItem, sPrehighlight);
2412      }
2413  };
2414  
2415  /**
2416   * Updates the text input box value with selected query result. If a delimiter
2417   * has been defined, then the value gets appended with the delimiter.
2418   *
2419   * @method _updateValue
2420   * @param elListItem {HTMLElement} The &lt;li&gt; element item with which to update the value.
2421   * @private
2422   */
2423  YAHOO.widget.AutoComplete.prototype._updateValue = function(elListItem) {
2424      if(!this.suppressInputUpdate) {    
2425          var elTextbox = this._elTextbox;
2426          var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null;
2427          var sResultMatch = elListItem._sResultMatch;
2428      
2429          // Calculate the new value
2430          var sNewValue = "";
2431          if(sDelimChar) {
2432              // Preserve selections from past queries
2433              sNewValue = this._sPastSelections;
2434              // Add new selection plus delimiter
2435              sNewValue += sResultMatch + sDelimChar;
2436              if(sDelimChar != " ") {
2437                  sNewValue += " ";
2438              }
2439          }
2440          else { 
2441              sNewValue = sResultMatch;
2442          }
2443          
2444          // Update input field
2445          elTextbox.value = sNewValue;
2446      
2447          // Scroll to bottom of textarea if necessary
2448          if(elTextbox.type == "textarea") {
2449              elTextbox.scrollTop = elTextbox.scrollHeight;
2450          }
2451      
2452          // Move cursor to end
2453          var end = elTextbox.value.length;
2454          this._selectText(elTextbox,end,end);
2455      
2456          this._elCurListItem = elListItem;
2457      }
2458  };
2459  
2460  /**
2461   * Selects a result item from the container
2462   *
2463   * @method _selectItem
2464   * @param elListItem {HTMLElement} The selected &lt;li&gt; element item.
2465   * @private
2466   */
2467  YAHOO.widget.AutoComplete.prototype._selectItem = function(elListItem) {
2468      this._bItemSelected = true;
2469      this._updateValue(elListItem);
2470      this._sPastSelections = this._elTextbox.value;
2471      this._clearInterval();
2472      this.itemSelectEvent.fire(this, elListItem, elListItem._oResultData);
2473      YAHOO.log("Item selected: " + YAHOO.lang.dump(elListItem._oResultData), "info", this.toString());
2474      this._toggleContainer(false);
2475  };
2476  
2477  /**
2478   * If an item is highlighted in the container, the right arrow key jumps to the
2479   * end of the textbox and selects the highlighted item, otherwise the container
2480   * is closed.
2481   *
2482   * @method _jumpSelection
2483   * @private
2484   */
2485  YAHOO.widget.AutoComplete.prototype._jumpSelection = function() {
2486      if(this._elCurListItem) {
2487          this._selectItem(this._elCurListItem);
2488      }
2489      else {
2490          this._toggleContainer(false);
2491      }
2492  };
2493  
2494  /**
2495   * Triggered by up and down arrow keys, changes the current highlighted
2496   * &lt;li&gt; element item. Scrolls container if necessary.
2497   *
2498   * @method _moveSelection
2499   * @param nKeyCode {Number} Code of key pressed.
2500   * @private
2501   */
2502  YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) {
2503      if(this._bContainerOpen) {
2504          // Determine current item's id number
2505          var elCurListItem = this._elCurListItem,
2506              nCurItemIndex = -1;
2507  
2508          if(elCurListItem) {
2509              nCurItemIndex = elCurListItem._nItemIndex;
2510          }
2511  
2512          var nNewItemIndex = (nKeyCode == 40) ?
2513                  (nCurItemIndex + 1) : (nCurItemIndex - 1);
2514  
2515          // Out of bounds
2516          if(nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) {
2517              return;
2518          }
2519  
2520          if(elCurListItem) {
2521              // Unhighlight current item
2522              this._toggleHighlight(elCurListItem, "from");
2523              this.itemArrowFromEvent.fire(this, elCurListItem);
2524              YAHOO.log("Item arrowed from: " + elCurListItem._nItemIndex, "info", this.toString());
2525          }
2526          if(nNewItemIndex == -1) {
2527             // Go back to query (remove type-ahead string)
2528              if(this.delimChar) {
2529                  this._elTextbox.value = this._sPastSelections + this._sCurQuery;
2530              }
2531              else {
2532                  this._elTextbox.value = this._sCurQuery;
2533              }
2534              return;
2535          }
2536          if(nNewItemIndex == -2) {
2537              // Close container
2538              this._toggleContainer(false);
2539              return;
2540          }
2541          
2542          var elNewListItem = this._elList.childNodes[nNewItemIndex],
2543  
2544          // Scroll the container if necessary
2545              elContent = this._elContent,
2546              sOF = YAHOO.util.Dom.getStyle(elContent,"overflow"),
2547              sOFY = YAHOO.util.Dom.getStyle(elContent,"overflowY"),
2548              scrollOn = ((sOF == "auto") || (sOF == "scroll") || (sOFY == "auto") || (sOFY == "scroll"));
2549          if(scrollOn && (nNewItemIndex > -1) &&
2550          (nNewItemIndex < this._nDisplayedItems)) {
2551              // User is keying down
2552              if(nKeyCode == 40) {
2553                  // Bottom of selected item is below scroll area...
2554                  if((elNewListItem.offsetTop+elNewListItem.offsetHeight) > (elContent.scrollTop + elContent.offsetHeight)) {
2555                      // Set bottom of scroll area to bottom of selected item
2556                      elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight;
2557                  }
2558                  // Bottom of selected item is above scroll area...
2559                  else if((elNewListItem.offsetTop+elNewListItem.offsetHeight) < elContent.scrollTop) {
2560                      // Set top of selected item to top of scroll area
2561                      elContent.scrollTop = elNewListItem.offsetTop;
2562  
2563                  }
2564              }
2565              // User is keying up
2566              else {
2567                  // Top of selected item is above scroll area
2568                  if(elNewListItem.offsetTop < elContent.scrollTop) {
2569                      // Set top of scroll area to top of selected item
2570                      this._elContent.scrollTop = elNewListItem.offsetTop;
2571                  }
2572                  // Top of selected item is below scroll area
2573                  else if(elNewListItem.offsetTop > (elContent.scrollTop + elContent.offsetHeight)) {
2574                      // Set bottom of selected item to bottom of scroll area
2575                      this._elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight;
2576                  }
2577              }
2578          }
2579  
2580          this._toggleHighlight(elNewListItem, "to");
2581          this.itemArrowToEvent.fire(this, elNewListItem);
2582          YAHOO.log("Item arrowed to " + elNewListItem._nItemIndex, "info", this.toString());
2583          if(this.typeAhead) {
2584              this._updateValue(elNewListItem);
2585              // Bug 2528552: Store as a selection
2586              this._sCurQuery = elNewListItem._sResultMatch;
2587          }
2588      }
2589  };
2590  
2591  /////////////////////////////////////////////////////////////////////////////
2592  //
2593  // Private event handlers
2594  //
2595  /////////////////////////////////////////////////////////////////////////////
2596  
2597  /**
2598   * Handles container mouseover events.
2599   *
2600   * @method _onContainerMouseover
2601   * @param v {HTMLEvent} The mouseover event.
2602   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2603   * @private
2604   */
2605  YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) {
2606      var elTarget = YAHOO.util.Event.getTarget(v);
2607      var elTag = elTarget.nodeName.toLowerCase();
2608      while(elTarget && (elTag != "table")) {
2609          switch(elTag) {
2610              case "body":
2611                  return;
2612              case "li":
2613                  if(oSelf.prehighlightClassName) {
2614                      oSelf._togglePrehighlight(elTarget,"mouseover");
2615                  }
2616                  else {
2617                      oSelf._toggleHighlight(elTarget,"to");
2618                  }
2619              
2620                  oSelf.itemMouseOverEvent.fire(oSelf, elTarget);
2621                  YAHOO.log("Item moused over " + elTarget._nItemIndex, "info", oSelf.toString());
2622                  break;
2623              case "div":
2624                  if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) {
2625                      oSelf._bOverContainer = true;
2626                      return;
2627                  }
2628                  break;
2629              default:
2630                  break;
2631          }
2632          
2633          elTarget = elTarget.parentNode;
2634          if(elTarget) {
2635              elTag = elTarget.nodeName.toLowerCase();
2636          }
2637      }
2638  };
2639  
2640  /**
2641   * Handles container mouseout events.
2642   *
2643   * @method _onContainerMouseout
2644   * @param v {HTMLEvent} The mouseout event.
2645   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2646   * @private
2647   */
2648  YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) {
2649      var elTarget = YAHOO.util.Event.getTarget(v);
2650      var elTag = elTarget.nodeName.toLowerCase();
2651      while(elTarget && (elTag != "table")) {
2652          switch(elTag) {
2653              case "body":
2654                  return;
2655              case "li":
2656                  if(oSelf.prehighlightClassName) {
2657                      oSelf._togglePrehighlight(elTarget,"mouseout");
2658                  }
2659                  else {
2660                      oSelf._toggleHighlight(elTarget,"from");
2661                  }
2662              
2663                  oSelf.itemMouseOutEvent.fire(oSelf, elTarget);
2664                  YAHOO.log("Item moused out " + elTarget._nItemIndex, "info", oSelf.toString());
2665                  break;
2666              case "ul":
2667                  oSelf._toggleHighlight(oSelf._elCurListItem,"to");
2668                  break;
2669              case "div":
2670                  if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) {
2671                      oSelf._bOverContainer = false;
2672                      return;
2673                  }
2674                  break;
2675              default:
2676                  break;
2677          }
2678  
2679          elTarget = elTarget.parentNode;
2680          if(elTarget) {
2681              elTag = elTarget.nodeName.toLowerCase();
2682          }
2683      }
2684  };
2685  
2686  /**
2687   * Handles container click events.
2688   *
2689   * @method _onContainerClick
2690   * @param v {HTMLEvent} The click event.
2691   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2692   * @private
2693   */
2694  YAHOO.widget.AutoComplete.prototype._onContainerClick = function(v,oSelf) {
2695      var elTarget = YAHOO.util.Event.getTarget(v);
2696      var elTag = elTarget.nodeName.toLowerCase();
2697      while(elTarget && (elTag != "table")) {
2698          switch(elTag) {
2699              case "body":
2700                  return;
2701              case "li":
2702                  // In case item has not been moused over
2703                  oSelf._toggleHighlight(elTarget,"to");
2704                  oSelf._selectItem(elTarget);
2705                  return;
2706              default:
2707                  break;
2708          }
2709  
2710          elTarget = elTarget.parentNode;
2711          if(elTarget) {
2712              elTag = elTarget.nodeName.toLowerCase();
2713          }
2714      }    
2715  };
2716  
2717  
2718  /**
2719   * Handles container scroll events.
2720   *
2721   * @method _onContainerScroll
2722   * @param v {HTMLEvent} The scroll event.
2723   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2724   * @private
2725   */
2726  YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) {
2727      oSelf._focus();
2728  };
2729  
2730  /**
2731   * Handles container resize events.
2732   *
2733   * @method _onContainerResize
2734   * @param v {HTMLEvent} The resize event.
2735   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2736   * @private
2737   */
2738  YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) {
2739      oSelf._toggleContainerHelpers(oSelf._bContainerOpen);
2740  };
2741  
2742  
2743  /**
2744   * Handles textbox keydown events of functional keys, mainly for UI behavior.
2745   *
2746   * @method _onTextboxKeyDown
2747   * @param v {HTMLEvent} The keydown event.
2748   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2749   * @private
2750   */
2751  YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) {
2752      var nKeyCode = v.keyCode;
2753  
2754      // Clear timeout
2755      if(oSelf._nTypeAheadDelayID != -1) {
2756          clearTimeout(oSelf._nTypeAheadDelayID);
2757      }
2758      
2759      switch (nKeyCode) {
2760          case 9: // tab
2761              if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) {
2762                  // select an item or clear out
2763                  if(oSelf._elCurListItem) {
2764                      if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {
2765                          if(oSelf._bContainerOpen) {
2766                              YAHOO.util.Event.stopEvent(v);
2767                          }
2768                      }
2769                      oSelf._selectItem(oSelf._elCurListItem);
2770                  }
2771                  else {
2772                      oSelf._toggleContainer(false);
2773                  }
2774              }
2775              break;
2776          case 13: // enter
2777              if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) {
2778                  if(oSelf._elCurListItem) {
2779                      if(oSelf._nKeyCode != nKeyCode) {
2780                          if(oSelf._bContainerOpen) {
2781                              YAHOO.util.Event.stopEvent(v);
2782                          }
2783                      }
2784                      oSelf._selectItem(oSelf._elCurListItem);
2785                  }
2786                  else {
2787                      oSelf._toggleContainer(false);
2788                  }
2789              }
2790              break;
2791          case 27: // esc
2792              oSelf._toggleContainer(false);
2793              return;
2794          case 39: // right
2795              oSelf._jumpSelection();
2796              break;
2797          case 38: // up
2798              if(oSelf._bContainerOpen) {
2799                  YAHOO.util.Event.stopEvent(v);
2800                  oSelf._moveSelection(nKeyCode);
2801              }
2802              break;
2803          case 40: // down
2804              if(oSelf._bContainerOpen) {
2805                  YAHOO.util.Event.stopEvent(v);
2806                  oSelf._moveSelection(nKeyCode);
2807              }
2808              break;
2809          default: 
2810              oSelf._bItemSelected = false;
2811              oSelf._toggleHighlight(oSelf._elCurListItem, "from");
2812  
2813              oSelf.textboxKeyEvent.fire(oSelf, nKeyCode);
2814              YAHOO.log("Textbox keyed", "info", oSelf.toString());
2815              break;
2816      }
2817  
2818      if(nKeyCode === 18){
2819          oSelf._enableIntervalDetection();
2820      }    
2821      oSelf._nKeyCode = nKeyCode;
2822  };
2823  
2824  /**
2825   * Handles textbox keypress events.
2826   * @method _onTextboxKeyPress
2827   * @param v {HTMLEvent} The keypress event.
2828   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2829   * @private
2830   */
2831  YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) {
2832      var nKeyCode = v.keyCode;
2833  
2834          // Expose only to non SF3 (bug 1978549) Mac browsers (bug 790337) and  Opera browsers (bug 583531),
2835          // where stopEvent is ineffective on keydown events 
2836          if(YAHOO.env.ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") != -1) && (YAHOO.env.ua.webkit < 420)) {
2837              switch (nKeyCode) {
2838              case 9: // tab
2839                  // select an item or clear out
2840                  if(oSelf._bContainerOpen) {
2841                      if(oSelf.delimChar) {
2842                          YAHOO.util.Event.stopEvent(v);
2843                      }
2844                      if(oSelf._elCurListItem) {
2845                          oSelf._selectItem(oSelf._elCurListItem);
2846                      }
2847                      else {
2848                          oSelf._toggleContainer(false);
2849                      }
2850                  }
2851                  break;
2852              case 13: // enter
2853                  if(oSelf._bContainerOpen) {
2854                      YAHOO.util.Event.stopEvent(v);
2855                      if(oSelf._elCurListItem) {
2856                          oSelf._selectItem(oSelf._elCurListItem);
2857                      }
2858                      else {
2859                          oSelf._toggleContainer(false);
2860                      }
2861                  }
2862                  break;
2863              default:
2864                  break;
2865              }
2866          }
2867  
2868          //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948)
2869          // Korean IME detected
2870          else if(nKeyCode == 229) {
2871              oSelf._enableIntervalDetection();
2872          }
2873  };
2874  
2875  /**
2876   * Handles textbox keyup events to trigger queries.
2877   *
2878   * @method _onTextboxKeyUp
2879   * @param v {HTMLEvent} The keyup event.
2880   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2881   * @private
2882   */
2883  YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) {
2884      var sText = this.value; //string in textbox
2885      
2886      // Check to see if any of the public properties have been updated
2887      oSelf._initProps();
2888  
2889      // Filter out chars that don't trigger queries
2890      var nKeyCode = v.keyCode;
2891      if(oSelf._isIgnoreKey(nKeyCode)) {
2892          return;
2893      }
2894  
2895      // Clear previous timeout
2896      if(oSelf._nDelayID != -1) {
2897          clearTimeout(oSelf._nDelayID);
2898      }
2899  
2900      // Set new timeout
2901      oSelf._nDelayID = setTimeout(function(){
2902              oSelf._sendQuery(sText);
2903          },(oSelf.queryDelay * 1000));
2904  };
2905  
2906  /**
2907   * Handles text input box receiving focus.
2908   *
2909   * @method _onTextboxFocus
2910   * @param v {HTMLEvent} The focus event.
2911   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2912   * @private
2913   */
2914  YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) {
2915      // Start of a new interaction
2916      if(!oSelf._bFocused) {
2917          oSelf._elTextbox.setAttribute("autocomplete","off");
2918          oSelf._bFocused = true;
2919          oSelf._sInitInputValue = oSelf._elTextbox.value;
2920          oSelf.textboxFocusEvent.fire(oSelf);
2921          YAHOO.log("Textbox focused", "info", oSelf.toString());
2922      }
2923  };
2924  
2925  /**
2926   * Handles text input box losing focus.
2927   *
2928   * @method _onTextboxBlur
2929   * @param v {HTMLEvent} The focus event.
2930   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2931   * @private
2932   */
2933  YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) {
2934      // Is a true blur
2935      if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) {
2936          // Current query needs to be validated as a selection
2937          if(!oSelf._bItemSelected) {
2938              var elMatchListItem = oSelf._textMatchesOption();
2939              // Container is closed or current query doesn't match any result
2940              if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && (elMatchListItem === null))) {
2941                  // Force selection is enabled so clear the current query
2942                  if(oSelf.forceSelection) {
2943                      oSelf._clearSelection();
2944                  }
2945                  // Treat current query as a valid selection
2946                  else {
2947                      oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery);
2948                      YAHOO.log("Unmatched item selected: " + oSelf._sCurQuery, "info", oSelf.toString());
2949                  }
2950              }
2951              // Container is open and current query matches a result
2952              else {
2953                  // Force a selection when textbox is blurred with a match
2954                  if(oSelf.forceSelection) {
2955                      oSelf._selectItem(elMatchListItem);
2956                  }
2957              }
2958          }
2959  
2960          oSelf._clearInterval();
2961          oSelf._bFocused = false;
2962          if(oSelf._sInitInputValue !== oSelf._elTextbox.value) {
2963              oSelf.textboxChangeEvent.fire(oSelf);
2964          }
2965          oSelf.textboxBlurEvent.fire(oSelf);
2966          YAHOO.log("Textbox blurred", "info", oSelf.toString());
2967  
2968          oSelf._toggleContainer(false);
2969      }
2970      // Not a true blur if it was a selection via mouse click
2971      else {
2972          oSelf._focus();
2973      }
2974  };
2975  
2976  /**
2977   * Handles window unload event.
2978   *
2979   * @method _onWindowUnload
2980   * @param v {HTMLEvent} The unload event.
2981   * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2982   * @private
2983   */
2984  YAHOO.widget.AutoComplete.prototype._onWindowUnload = function(v,oSelf) {
2985      if(oSelf && oSelf._elTextbox && oSelf.allowBrowserAutocomplete) {
2986          oSelf._elTextbox.setAttribute("autocomplete","on");
2987      }
2988  };
2989  
2990  /////////////////////////////////////////////////////////////////////////////
2991  //
2992  // Deprecated for Backwards Compatibility
2993  //
2994  /////////////////////////////////////////////////////////////////////////////
2995  /**
2996   * @method doBeforeSendQuery
2997   * @deprecated Use generateRequest.
2998   */
2999  YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery = function(sQuery) {
3000      return this.generateRequest(sQuery);
3001  };
3002  
3003  /**
3004   * @method getListItems
3005   * @deprecated Use getListEl().childNodes.
3006   */
3007  YAHOO.widget.AutoComplete.prototype.getListItems = function() {
3008      var allListItemEls = [],
3009          els = this._elList.childNodes;
3010      for(var i=els.length-1; i>=0; i--) {
3011          allListItemEls[i] = els[i];
3012      }
3013      return allListItemEls;
3014  };
3015  
3016  /////////////////////////////////////////////////////////////////////////
3017  //
3018  // Private static methods
3019  //
3020  /////////////////////////////////////////////////////////////////////////
3021  
3022  /**
3023   * Clones object literal or array of object literals.
3024   *
3025   * @method AutoComplete._cloneObject
3026   * @param o {Object} Object.
3027   * @private
3028   * @static     
3029   */
3030  YAHOO.widget.AutoComplete._cloneObject = function(o) {
3031      if(!YAHOO.lang.isValue(o)) {
3032          return o;
3033      }
3034      
3035      var copy = {};
3036      
3037      if(YAHOO.lang.isFunction(o)) {
3038          copy = o;
3039      }
3040      else if(YAHOO.lang.isArray(o)) {
3041          var array = [];
3042          for(var i=0,len=o.length;i<len;i++) {
3043              array[i] = YAHOO.widget.AutoComplete._cloneObject(o[i]);
3044          }
3045          copy = array;
3046      }
3047      else if(YAHOO.lang.isObject(o)) { 
3048          for (var x in o){
3049              if(YAHOO.lang.hasOwnProperty(o, x)) {
3050                  if(YAHOO.lang.isValue(o[x]) && YAHOO.lang.isObject(o[x]) || YAHOO.lang.isArray(o[x])) {
3051                      copy[x] = YAHOO.widget.AutoComplete._cloneObject(o[x]);
3052                  }
3053                  else {
3054                      copy[x] = o[x];
3055                  }
3056              }
3057          }
3058      }
3059      else {
3060          copy = o;
3061      }
3062  
3063      return copy;
3064  };
3065  
3066  
3067  
3068  
3069  YAHOO.register("autocomplete", YAHOO.widget.AutoComplete, {version: "2.9.0", build: "2800"});
3070  
3071  }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-autocomplete", "yui2-datasource"], "optional": ["yui2-animation", "yui2-connection"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1