[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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