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