PageSourceSearch

http://siberia-web.ru/base/collie/js/yui/autocomplete.js

js siberia-web.ru collected 2026-10-04 21:55:08 UTC 94,673 bytes, 2,873 lines download raw bytes

1/*
2Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 2.6.0
6*/
7/////////////////////////////////////////////////////////////////////////////
8//
9// YAHOO.widget.DataSource Backwards Compatibility
10//
11/////////////////////////////////////////////////////////////////////////////
12
13YAHOO.widget.DS_JSArray = YAHOO.util.LocalDataSource;
14
15YAHOO.widget.DS_JSFunction = YAHOO.util.FunctionDataSource;
16
17YAHOO.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
23YAHOO.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 */
72YAHOO.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 */
235YAHOO.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 */
245YAHOO.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 */
255YAHOO.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 */
266YAHOO.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 */
281YAHOO.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 */
292YAHOO.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 */
301YAHOO.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 */
314YAHOO.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 */
325YAHOO.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 */
335YAHOO.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 */
344YAHOO.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 */
352YAHOO.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 */
364YAHOO.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 */
374YAHOO.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 */
385YAHOO.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 */
395YAHOO.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 */
405YAHOO.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 */
414YAHOO.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 */
426YAHOO.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 */
441YAHOO.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 */
452YAHOO.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 */
464YAHOO.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 */
473YAHOO.widget.AutoComplete.prototype.useShadow = false;
474
475/**
476 * Whether or not the input field should be updated with selections.
477 *
478 * @property supressInputUpdate
479 * @type Boolean
480 * @default false
481 */
482YAHOO.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 */
493YAHOO.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 */
505YAHOO.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 */
519YAHOO.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 */
529YAHOO.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 */
539YAHOO.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 */
549YAHOO.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 */
559YAHOO.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 */
569YAHOO.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 */
580YAHOO.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 */
596YAHOO.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 */
612YAHOO.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 */
628YAHOO.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 */
649YAHOO.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;
665 tag with a class of "yui-ac-bd".
666 *
667 * @method setBody
668 * @param sBody {String} HTML markup for results container body.
669 */
670YAHOO.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*/
695YAHOO.widget.AutoComplete.prototype.generateRequest = function(sQuery) {
696    var dataType = this.dataSource.dataType;
697    
698    // Transform query string in to a request for remote data
699    // By default, local data doesn't need a transformation, just passes along the query as is.
700    if(dataType === YAHOO.util.DataSourceBase.TYPE_XHR) {
701        // By default, XHR GET requests look like "{scriptURI}?{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
702        if(!this.dataSource.connMethodPost) {
703            sQuery = (this.queryQuestionMark ? "?" : "") + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
704                (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");        
705        }
706        // By default, XHR POST bodies are sent to the {scriptURI} like "{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
707        else {
708            sQuery = (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
709                (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");
710        }
711    }
712    // By default, remote script node requests look like "{scriptURI}&{scriptCallbackParam}={callbackString}&{scriptQueryParam}={sQuery}&{scriptQueryAppend}"
713    else if(dataType === YAHOO.util.DataSourceBase.TYPE_SCRIPTNODE) {
714        sQuery = "&" + (this.dataSource.scriptQueryParam || "query") + "=" + sQuery + 
715            (this.dataSource.scriptQueryAppend ? ("&" + this.dataSource.scriptQueryAppend) : "");    
716    }
717    
718    return sQuery;
719};
720
721/**
722 * Makes query request to the DataSource.
723 *
724 * @method sendQuery
725 * @param sQuery {String} Query string.
726 */
727YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
728    // Adjust programatically sent queries to look like they input by user
729    // when delimiters are enabled
730    var newQuery = (this.delimChar) ? this._elTextbox.value + sQuery : sQuery;
731    this._sendQuery(newQuery);
732};
733
734/**
735 * Collapses container.
736 *
737 * @method collapseContainer
738 */
739YAHOO.widget.AutoComplete.prototype.collapseContainer = function() {
740    this._toggleContainer(false);
741};
742
743/**
744 * Handles subset matching for when queryMatchSubset is enabled.
745 *
746 * @method getSubsetMatches
747 * @param sQuery {String} Query string.
748 * @return {Object} oParsedResponse or null. 
749 */
750YAHOO.widget.AutoComplete.prototype.getSubsetMatches = function(sQuery) {
751    var subQuery, oCachedResponse, subRequest;
752    // Loop through substrings of each cached element's query property...
753    for(var i = sQuery.length; i >= this.minQueryLength ; i--) {
754        subRequest = this.generateRequest(sQuery.substr(0,i));
755        this.dataRequestEvent.fire(this, subQuery, subRequest);
756        
757        // If a substring of the query is found in the cache
758        oCachedResponse = this.dataSource.getCachedResponse(subRequest);
759        if(oCachedResponse) {
760            return this.filterResults.apply(this.dataSource, [sQuery, oCachedResponse, oCachedResponse, {scope:this}]);
761        }
762    }
763    return null;
764};
765
766/**
767 * Executed by DataSource (within DataSource scope via doBeforeParseData()) to
768 * handle responseStripAfter cleanup.
769 *
770 * @method preparseRawResponse
771 * @param sQuery {String} Query string.
772 * @return {Object} oParsedResponse or null. 
773 */
774YAHOO.widget.AutoComplete.prototype.preparseRawResponse = function(oRequest, oFullResponse, oCallback) {
775    var nEnd = ((this.responseStripAfter !== "") && (oFullResponse.indexOf)) ?
776        oFullResponse.indexOf(this.responseStripAfter) : -1;
777    if(nEnd != -1) {
778        oFullResponse = oFullResponse.substring(0,nEnd);
779    }
780    return oFullResponse;
781};
782
783/**
784 * Executed by DataSource (within DataSource scope via doBeforeCallback()) to
785 * filter results through a simple client-side matching algorithm. 
786 *
787 * @method filterResults
788 * @param sQuery {String} Original request.
789 * @param oFullResponse {Object} Full response object.
790 * @param oParsedResponse {Object} Parsed response object.
791 * @param oCallback {Object} Callback object. 
792 * @return {Object} Filtered response object.
793 */
794
795YAHOO.widget.AutoComplete.prototype.filterResults = function(sQuery, oFullResponse, oParsedResponse, oCallback) {
796    // Only if a query string is available to match against
797    if(sQuery && sQuery !== "") {
798        // First make a copy of the oParseResponse
799        oParsedResponse = YAHOO.widget.AutoComplete._cloneObject(oParsedResponse);
800        
801        var oAC = oCallback.scope,
802            oDS = this,
803            allResults = oParsedResponse.results, // the array of results
804            filteredResults = [], // container for filtered results
805            bMatchFound = false,
806            bMatchCase = (oDS.queryMatchCase || oAC.queryMatchCase), // backward compat
807            bMatchContains = (oDS.queryMatchContains || oAC.queryMatchContains); // backward compat
808            
809        // Loop through each result object...
810        for(var i = allResults.length-1; i >= 0; i--) {
811            var oResult = allResults[i];
812
813            // Grab the data to match against from the result object...
814            var sResult = null;
815            
816            // Result object is a simple string already
817            if(YAHOO.lang.isString(oResult)) {
818                sResult = oResult;
819            }
820            // Result object is an array of strings
821            else if(YAHOO.lang.isArray(oResult)) {
822                sResult = oResult[0];
823            
824            }
825            // Result object is an object literal of strings
826            else if(this.responseSchema.fields) {
827                var key = this.responseSchema.fields[0].key || this.responseSchema.fields[0];
828                sResult = oResult[key];
829            }
830            // Backwards compatibility
831            else if(this.key) {
832                sResult = oResult[this.key];
833            }
834            
835            if(YAHOO.lang.isString(sResult)) {
836                
837                var sKeyIndex = (bMatchCase) ?
838                sResult.indexOf(decodeURIComponent(sQuery)) :
839                sResult.toLowerCase().indexOf(decodeURIComponent(sQuery).toLowerCase());
840
841                // A STARTSWITH match is when the query is found at the beginning of the key string...
842                if((!bMatchContains && (sKeyIndex === 0)) ||
843                // A CONTAINS match is when the query is found anywhere within the key string...
844                (bMatchContains && (sKeyIndex > -1))) {
845                    // Stash the match
846                    filteredResults.unshift(oResult);
847                }
848            }
849        }
850        oParsedResponse.results = filteredResults;
851    }
852    else {
853    }
854    
855    return oParsedResponse;
856};
857
858/**
859 * Handles response for display. This is the callback function method passed to
860 * YAHOO.util.DataSourceBase#sendRequest so results from the DataSource are
861 * returned to the AutoComplete instance.
862 *
863 * @method handleResponse
864 * @param sQuery {String} Original request.
865 * @param oResponse {Object} Response object.
866 * @param oPayload {MIXED} (optional) Additional argument(s)
867 */
868YAHOO.widget.AutoComplete.prototype.handleResponse = function(sQuery, oResponse, oPayload) {
869    if((this instanceof YAHOO.widget.AutoComplete) && this._sName) {
870        this._populateList(sQuery, oResponse, oPayload);
871    }
872};
873
874/**
875 * Overridable method called before container is loaded with result data.
876 *
877 * @method doBeforeLoadData
878 * @param sQuery {String} Original request.
879 * @param oResponse {Object} Response object.
880 * @param oPayload {MIXED} (optional) Additional argument(s)
881 * @return {Boolean} Return true to continue loading data, false to cancel.
882 */
883YAHOO.widget.AutoComplete.prototype.doBeforeLoadData = function(sQuery, oResponse, oPayload) {
884    return true;
885};
886
887/**
888 * Overridable method that returns HTML markup for one result to be populated
889 * as innerHTML of an &lt;LI&gt; element. 
890 *
891 * @method formatResult
892 * @param oResultData {Object} Result data object.
893 * @param sQuery {String} The corresponding query string.
894 * @param sResultMatch {HTMLElement} The current query string. 
895 * @return {String} HTML markup of formatted result data.
896 */
897YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultData, sQuery, sResultMatch) {
898    var sMarkup = (sResultMatch) ? sResultMatch : "";
899    return sMarkup;
900};
901
902/**
903 * Overridable method called before container expands allows implementers to access data
904 * and DOM elements.
905 *
906 * @method doBeforeExpandContainer
907 * @param elTextbox {HTMLElement} The text input box.
908 * @param elContainer {HTMLElement} The container element.
909 * @param sQuery {String} The query string.
910 * @param aResults {Object[]}  An array of query results.
911 * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
912 */
913YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(elTextbox, elContainer, sQuery, aResults) {
914    return true;
915};
916
917
918/**
919 * Nulls out the entire AutoComplete instance and related objects, removes attached
920 * event listeners, and clears out DOM elements inside the container. After
921 * calling this method, the instance reference should be expliclitly nulled by
922 * implementer, as in myDataTable = null. Use with caution!
923 *
924 * @method destroy
925 */
926YAHOO.widget.AutoComplete.prototype.destroy = function() {
927    var instanceName = this.toString();
928    var elInput = this._elTextbox;
929    var elContainer = this._elContainer;
930
931    // Unhook custom events
932    this.textboxFocusEvent.unsubscribeAll();
933    this.textboxKeyEvent.unsubscribeAll();
934    this.dataRequestEvent.unsubscribeAll();
935    this.dataReturnEvent.unsubscribeAll();
936    this.dataErrorEvent.unsubscribeAll();
937    this.containerPopulateEvent.unsubscribeAll();
938    this.containerExpandEvent.unsubscribeAll();
939    this.typeAheadEvent.unsubscribeAll();
940    this.itemMouseOverEvent.unsubscribeAll();
941    this.itemMouseOutEvent.unsubscribeAll();
942    this.itemArrowToEvent.unsubscribeAll();
943    this.itemArrowFromEvent.unsubscribeAll();
944    this.itemSelectEvent.unsubscribeAll();
945    this.unmatchedItemSelectEvent.unsubscribeAll();
946    this.selectionEnforceEvent.unsubscribeAll();
947    this.containerCollapseEvent.unsubscribeAll();
948    this.textboxBlurEvent.unsubscribeAll();
949    this.textboxChangeEvent.unsubscribeAll();
950
951    // Unhook DOM events
952    YAHOO.util.Event.purgeElement(elInput, true);
953    YAHOO.util.Event.purgeElement(elContainer, true);
954
955    // Remove DOM elements
956    elContainer.innerHTML = "";
957
958    // Null out objects
959    for(var key in this) {
960        if(YAHOO.lang.hasOwnProperty(this, key)) {
961            this[key] = null;
962        }
963    }
964
965};
966
967/////////////////////////////////////////////////////////////////////////////
968//
969// Public events
970//
971/////////////////////////////////////////////////////////////////////////////
972
973/**
974 * Fired when the input field receives focus.
975 *
976 * @event textboxFocusEvent
977 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
978 */
979YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
980
981/**
982 * Fired when the input field receives key input.
983 *
984 * @event textboxKeyEvent
985 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
986 * @param nKeycode {Number} The keycode number.
987 */
988YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
989
990/**
991 * Fired when the AutoComplete instance makes a request to the DataSource.
992 * 
993 * @event dataRequestEvent
994 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
995 * @param sQuery {String} The query string. 
996 * @param oRequest {Object} The request.
997 */
998YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
999
1000/**
1001 * Fired when the AutoComplete instance receives query results from the data
1002 * source.
1003 *
1004 * @event dataReturnEvent
1005 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1006 * @param sQuery {String} The query string.
1007 * @param aResults {Object[]} Results array.
1008 */
1009YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
1010
1011/**
1012 * Fired when the AutoComplete instance does not receive query results from the
1013 * DataSource due to an error.
1014 *
1015 * @event dataErrorEvent
1016 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1017 * @param sQuery {String} The query string.
1018 */
1019YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
1020
1021/**
1022 * Fired when the results container is populated.
1023 *
1024 * @event containerPopulateEvent
1025 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1026 */
1027YAHOO.widget.AutoComplete.prototype.containerPopulateEvent = null;
1028
1029/**
1030 * Fired when the results container is expanded.
1031 *
1032 * @event containerExpandEvent
1033 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1034 * @param sQuery {String} The query string.
1035 * @param aResults {Object[]} Results array.
1036 */
1037YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
1038
1039/**
1040 * Fired when the input field has been prefilled by the type-ahead
1041 * feature. 
1042 *
1043 * @event typeAheadEvent
1044 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1045 * @param sQuery {String} The query string.
1046 * @param sPrefill {String} The prefill string.
1047 */
1048YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
1049
1050/**
1051 * Fired when result item has been moused over.
1052 *
1053 * @event itemMouseOverEvent
1054 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1055 * @param elItem {HTMLElement} The &lt;li&gt element item moused to.
1056 */
1057YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
1058
1059/**
1060 * Fired when result item has been moused out.
1061 *
1062 * @event itemMouseOutEvent
1063 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1064 * @param elItem {HTMLElement} The &lt;li&gt; element item moused from.
1065 */
1066YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
1067
1068/**
1069 * Fired when result item has been arrowed to. 
1070 *
1071 * @event itemArrowToEvent
1072 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1073 * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed to.
1074 */
1075YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
1076
1077/**
1078 * Fired when result item has been arrowed away from.
1079 *
1080 * @event itemArrowFromEvent
1081 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1082 * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed from.
1083 */
1084YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
1085
1086/**
1087 * Fired when an item is selected via mouse click, ENTER key, or TAB key.
1088 *
1089 * @event itemSelectEvent
1090 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1091 * @param elItem {HTMLElement} The selected &lt;li&gt; element item.
1092 * @param oData {Object} The data returned for the item, either as an object,
1093 * or mapped from the schema into an array.
1094 */
1095YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
1096
1097/**
1098 * Fired when a user selection does not match any of the displayed result items.
1099 *
1100 * @event unmatchedItemSelectEvent
1101 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1102 * @param sSelection {String} The selected string.  
1103 */
1104YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
1105
1106/**
1107 * Fired if forceSelection is enabled and the user's input has been cleared
1108 * because it did not match one of the returned query results.
1109 *
1110 * @event selectionEnforceEvent
1111 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1112 */
1113YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
1114
1115/**
1116 * Fired when the results container is collapsed.
1117 *
1118 * @event containerCollapseEvent
1119 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1120 */
1121YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
1122
1123/**
1124 * Fired when the input field loses focus.
1125 *
1126 * @event textboxBlurEvent
1127 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1128 */
1129YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
1130
1131/**
1132 * Fired when the input field value has changed when it loses focus.
1133 *
1134 * @event textboxChangeEvent
1135 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1136 */
1137YAHOO.widget.AutoComplete.prototype.textboxChangeEvent = null;
1138
1139/////////////////////////////////////////////////////////////////////////////
1140//
1141// Private member variables
1142//
1143/////////////////////////////////////////////////////////////////////////////
1144
1145/**
1146 * Internal class variable to index multiple AutoComplete instances.
1147 *
1148 * @property _nIndex
1149 * @type Number
1150 * @default 0
1151 * @private
1152 */
1153YAHOO.widget.AutoComplete._nIndex = 0;
1154
1155/**
1156 * Name of AutoComplete instance.
1157 *
1158 * @property _sName
1159 * @type String
1160 * @private
1161 */
1162YAHOO.widget.AutoComplete.prototype._sName = null;
1163
1164/**
1165 * Text input field DOM element.
1166 *
1167 * @property _elTextbox
1168 * @type HTMLElement
1169 * @private
1170 */
1171YAHOO.widget.AutoComplete.prototype._elTextbox = null;
1172
1173/**
1174 * Container DOM element.
1175 *
1176 * @property _elContainer
1177 * @type HTMLElement
1178 * @private
1179 */
1180YAHOO.widget.AutoComplete.prototype._elContainer = null;
1181
1182/**
1183 * Reference to content element within container element.
1184 *
1185 * @property _elContent
1186 * @type HTMLElement
1187 * @private
1188 */
1189YAHOO.widget.AutoComplete.prototype._elContent = null;
1190
1191/**
1192 * Reference to header element within content element.
1193 *
1194 * @property _elHeader
1195 * @type HTMLElement
1196 * @private
1197 */
1198YAHOO.widget.AutoComplete.prototype._elHeader = null;
1199
1200/**
1201 * Reference to body element within content element.
1202 *
1203 * @property _elBody
1204 * @type HTMLElement
1205 * @private
1206 */
1207YAHOO.widget.AutoComplete.prototype._elBody = null;
1208
1209/**
1210 * Reference to footer element within content element.
1211 *
1212 * @property _elFooter
1213 * @type HTMLElement
1214 * @private
1215 */
1216YAHOO.widget.AutoComplete.prototype._elFooter = null;
1217
1218/**
1219 * Reference to shadow element within container element.
1220 *
1221 * @property _elShadow
1222 * @type HTMLElement
1223 * @private
1224 */
1225YAHOO.widget.AutoComplete.prototype._elShadow = null;
1226
1227/**
1228 * Reference to iframe element within container element.
1229 *
1230 * @property _elIFrame
1231 * @type HTMLElement
1232 * @private
1233 */
1234YAHOO.widget.AutoComplete.prototype._elIFrame = null;
1235
1236/**
1237 * Whether or not the input field is currently in focus. If query results come back
1238 * but the user has already moved on, do not proceed with auto complete behavior.
1239 *
1240 * @property _bFocused
1241 * @type Boolean
1242 * @private
1243 */
1244YAHOO.widget.AutoComplete.prototype._bFocused = null;
1245
1246/**
1247 * Animation instance for container expand/collapse.
1248 *
1249 * @property _oAnim
1250 * @type Boolean
1251 * @private
1252 */
1253YAHOO.widget.AutoComplete.prototype._oAnim = null;
1254
1255/**
1256 * Whether or not the results container is currently open.
1257 *
1258 * @property _bContainerOpen
1259 * @type Boolean
1260 * @private
1261 */
1262YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
1263
1264/**
1265 * Whether or not the mouse is currently over the results
1266 * container. This is necessary in order to prevent clicks on container items
1267 * from being text input field blur events.
1268 *
1269 * @property _bOverContainer
1270 * @type Boolean
1271 * @private
1272 */
1273YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
1274
1275/**
1276 * Internal reference to &lt;ul&gt; elements that contains query results within the
1277 * results container.
1278 *
1279 * @property _elList
1280 * @type HTMLElement
1281 * @private
1282 */
1283YAHOO.widget.AutoComplete.prototype._elList = null;
1284
1285/*
1286 * Array of &lt;li&gt; elements references that contain query results within the
1287 * results container.
1288 *
1289 * @property _aListItemEls
1290 * @type HTMLElement[]
1291 * @private
1292 */
1293//YAHOO.widget.AutoComplete.prototype._aListItemEls = null;
1294
1295/**
1296 * Number of &lt;li&gt; elements currently displayed in results container.
1297 *
1298 * @property _nDisplayedItems
1299 * @type Number
1300 * @private
1301 */
1302YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
1303
1304/*
1305 * Internal count of &lt;li&gt; elements displayed and hidden in results container.
1306 *
1307 * @property _maxResultsDisplayed
1308 * @type Number
1309 * @private
1310 */
1311//YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
1312
1313/**
1314 * Current query string
1315 *
1316 * @property _sCurQuery
1317 * @type String
1318 * @private
1319 */
1320YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
1321
1322/**
1323 * Selections from previous queries (for saving delimited queries).
1324 *
1325 * @property _sPastSelections
1326 * @type String
1327 * @default "" 
1328 * @private
1329 */
1330YAHOO.widget.AutoComplete.prototype._sPastSelections = "";
1331
1332/**
1333 * Stores initial input value used to determine if textboxChangeEvent should be fired.
1334 *
1335 * @property _sInitInputValue
1336 * @type String
1337 * @private
1338 */
1339YAHOO.widget.AutoComplete.prototype._sInitInputValue = null;
1340
1341/**
1342 * Pointer to the currently highlighted &lt;li&gt; element in the container.
1343 *
1344 * @property _elCurListItem
1345 * @type HTMLElement
1346 * @private
1347 */
1348YAHOO.widget.AutoComplete.prototype._elCurListItem = null;
1349
1350/**
1351 * Whether or not an item has been selected since the container was populated
1352 * with results. Reset to false by _populateList, and set to true when item is
1353 * selected.
1354 *
1355 * @property _bItemSelected
1356 * @type Boolean
1357 * @private
1358 */
1359YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
1360
1361/**
1362 * Key code of the last key pressed in textbox.
1363 *
1364 * @property _nKeyCode
1365 * @type Number
1366 * @private
1367 */
1368YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
1369
1370/**
1371 * Delay timeout ID.
1372 *
1373 * @property _nDelayID
1374 * @type Number
1375 * @private
1376 */
1377YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
1378
1379/**
1380 * TypeAhead delay timeout ID.
1381 *
1382 * @property _nTypeAheadDelayID
1383 * @type Number
1384 * @private
1385 */
1386YAHOO.widget.AutoComplete.prototype._nTypeAheadDelayID = -1;
1387
1388/**
1389 * Src to iFrame used when useIFrame = true. Supports implementations over SSL
1390 * as well.
1391 *
1392 * @property _iFrameSrc
1393 * @type String
1394 * @private
1395 */
1396YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
1397
1398/**
1399 * For users typing via certain IMEs, queries must be triggered by intervals,
1400 * since key events yet supported across all browsers for all IMEs.
1401 *
1402 * @property _queryInterval
1403 * @type Object
1404 * @private
1405 */
1406YAHOO.widget.AutoComplete.prototype._queryInterval = null;
1407
1408/**
1409 * Internal tracker to last known textbox value, used to determine whether or not
1410 * to trigger a query via interval for certain IME users.
1411 *
1412 * @event _sLastTextboxValue
1413 * @type String
1414 * @private
1415 */
1416YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
1417
1418/////////////////////////////////////////////////////////////////////////////
1419//
1420// Private methods
1421//
1422/////////////////////////////////////////////////////////////////////////////
1423
1424/**
1425 * Updates and validates latest public config properties.
1426 *
1427 * @method __initProps
1428 * @private
1429 */
1430YAHOO.widget.AutoComplete.prototype._initProps = function() {
1431    // Correct any invalid values
1432    var minQueryLength = this.minQueryLength;
1433    if(!YAHOO.lang.isNumber(minQueryLength)) {
1434        this.minQueryLength = 1;
1435    }
1436    var maxResultsDisplayed = this.maxResultsDisplayed;
1437    if(!YAHOO.lang.isNumber(maxResultsDisplayed) || (maxResultsDisplayed < 1)) {
1438        this.maxResultsDisplayed = 10;
1439    }
1440    var queryDelay = this.queryDelay;
1441    if(!YAHOO.lang.isNumber(queryDelay) || (queryDelay < 0)) {
1442        this.queryDelay = 0.2;
1443    }
1444    var typeAheadDelay = this.typeAheadDelay;
1445    if(!YAHOO.lang.isNumber(typeAheadDelay) || (typeAheadDelay < 0)) {
1446        this.typeAheadDelay = 0.2;
1447    }
1448    var delimChar = this.delimChar;
1449    if(YAHOO.lang.isString(delimChar) && (delimChar.length > 0)) {
1450        this.delimChar = [delimChar];
1451    }
1452    else if(!YAHOO.lang.isArray(delimChar)) {
1453        this.delimChar = null;
1454    }
1455    var animSpeed = this.animSpeed;
1456    if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
1457        if(!YAHOO.lang.isNumber(animSpeed) || (animSpeed < 0)) {
1458            this.animSpeed = 0.3;
1459        }
1460        if(!this._oAnim ) {
1461            this._oAnim = new YAHOO.util.Anim(this._elContent, {}, this.animSpeed);
1462        }
1463        else {
1464            this._oAnim.duration = this.animSpeed;
1465        }
1466    }
1467    if(this.forceSelection && delimChar) {
1468    }
1469};
1470
1471/**
1472 * Initializes the results container helpers if they are enabled and do
1473 * not exist
1474 *
1475 * @method _initContainerHelperEls
1476 * @private
1477 */
1478YAHOO.widget.AutoComplete.prototype._initContainerHelperEls = function() {
1479    if(this.useShadow && !this._elShadow) {
1480        var elShadow = document.createElement("div");
1481        elShadow.className = "yui-ac-shadow";
1482        elShadow.style.width = 0;
1483        elShadow.style.height = 0;
1484        this._elShadow = this._elContainer.appendChild(elShadow);
1485    }
1486    if(this.useIFrame && !this._elIFrame) {
1487        var elIFrame = document.createElement("iframe");
1488        elIFrame.src = this._iFrameSrc;
1489        elIFrame.frameBorder = 0;
1490        elIFrame.scrolling = "no";
1491        elIFrame.style.position = "absolute";
1492        elIFrame.style.width = 0;
1493        elIFrame.style.height = 0;
1494        elIFrame.tabIndex = -1;
1495        elIFrame.style.padding = 0;
1496        this._elIFrame = this._elContainer.appendChild(elIFrame);
1497    }
1498};
1499
1500/**
1501 * Initializes the results container once at object creation
1502 *
1503 * @method _initContainerEl
1504 * @private
1505 */
1506YAHOO.widget.AutoComplete.prototype._initContainerEl = function() {
1507    YAHOO.util.Dom.addClass(this._elContainer, "yui-ac-container");
1508    
1509    if(!this._elContent) {
1510        // The elContent div is assigned DOM listeners and 
1511        // helps size the iframe and shadow properly
1512        var elContent = document.createElement("div");
1513        elContent.className = "yui-ac-content";
1514        elContent.style.display = "none";
1515
1516        this._elContent = this._elContainer.appendChild(elContent);
1517
1518        var elHeader = document.createElement("div");
1519        elHeader.className = "yui-ac-hd";
1520        elHeader.style.display = "none";
1521        this._elHeader = this._elContent.appendChild(elHeader);
1522
1523        var elBody = document.createElement("div");
1524        elBody.className = "yui-ac-bd";
1525        this._elBody = this._elContent.appendChild(elBody);
1526
1527        var elFooter = document.createElement("div");
1528        elFooter.className = "yui-ac-ft";
1529        elFooter.style.display = "none";
1530        this._elFooter = this._elContent.appendChild(elFooter);
1531    }
1532    else {
1533    }
1534};
1535
1536/**
1537 * Clears out contents of container body and creates up to
1538 * YAHOO.widget.AutoComplete#maxResultsDisplayed &lt;li&gt; elements in an
1539 * &lt;ul&gt; element.
1540 *
1541 * @method _initListEl
1542 * @private
1543 */
1544YAHOO.widget.AutoComplete.prototype._initListEl = function() {
1545    var nListLength = this.maxResultsDisplayed;
1546    
1547    var elList = this._elList || document.createElement("ul");
1548    var elListItem;
1549    while(elList.childNodes.length < nListLength) {
1550        elListItem = document.createElement("li");
1551        elListItem.style.display = "none";
1552        elListItem._nItemIndex = elList.childNodes.length;
1553        elList.appendChild(elListItem);
1554    }
1555    if(!this._elList) {
1556        var elBody = this._elBody;
1557        YAHOO.util.Event.purgeElement(elBody, true);
1558        elBody.innerHTML = "";
1559        this._elList = elBody.appendChild(elList);
1560    }
1561    
1562};
1563
1564/**
1565 * Enables interval detection for IME support.
1566 *
1567 * @method _enableIntervalDetection
1568 * @re 
1569 * @private
1570 */
1571YAHOO.widget.AutoComplete.prototype._enableIntervalDetection = function() {
1572    var oSelf = this;
1573    if(!oSelf._queryInterval && oSelf.queryInterval) {
1574        oSelf._queryInterval = setInterval(function() { oSelf._onInterval(); }, oSelf.queryInterval);
1575    }
1576};
1577
1578/**
1579 * Enables query triggers based on text input detection by intervals (rather
1580 * than by key events).
1581 *
1582 * @method _onInterval
1583 * @private
1584 */
1585YAHOO.widget.AutoComplete.prototype._onInterval = function() {
1586    var currValue = this._elTextbox.value;
1587    var lastValue = this._sLastTextboxValue;
1588    if(currValue != lastValue) {
1589        this._sLastTextboxValue = currValue;
1590        this._sendQuery(currValue);
1591    }
1592};
1593
1594/**
1595 * Cancels text input detection by intervals.
1596 *
1597 * @method _clearInterval
1598 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
1599 * @private
1600 */
1601YAHOO.widget.AutoComplete.prototype._clearInterval = function() {
1602    if(this._queryInterval) {
1603        clearInterval(this._queryInterval);
1604        this._queryInterval = null;
1605    }
1606};
1607
1608/**
1609 * Whether or not key is functional or should be ignored. Note that the right
1610 * arrow key is NOT an ignored key since it triggers queries for certain intl
1611 * charsets.
1612 *
1613 * @method _isIgnoreKey
1614 * @param nKeycode {Number} Code of key pressed.
1615 * @return {Boolean} True if key should be ignored, false otherwise.
1616 * @private
1617 */
1618YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) {
1619    if((nKeyCode == 9) || (nKeyCode == 13)  || // tab, enter
1620            (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl
1621            (nKeyCode >= 18 && nKeyCode <= 20) || // alt, pause/break,caps lock
1622            (nKeyCode == 27) || // esc
1623            (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end
1624            /*(nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up
1625            (nKeyCode == 40) || // down*/
1626            (nKeyCode >= 36 && nKeyCode <= 40) || // home,left,up, right, down
1627            (nKeyCode >= 44 && nKeyCode <= 45) || // print screen,insert
1628            (nKeyCode == 229) // Bug 2041973: Korean XP fires 2 keyup events, the key and 229
1629        ) { 
1630        return true;
1631    }
1632    return false;
1633};
1634
1635/**
1636 * Makes query request to the DataSource.
1637 *
1638 * @method _sendQuery
1639 * @param sQuery {String} Query string.
1640 * @private
1641 */
1642YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) {
1643    // Widget has been effectively turned off
1644    if(this.minQueryLength < 0) {
1645        this._toggleContainer(false);
1646        return;
1647    }
1648    // Delimiter has been enabled
1649    var aDelimChar = (this.delimChar) ? this.delimChar : null;
1650    if(aDelimChar) {
1651        // Loop through all possible delimiters and find the rightmost one in the query
1652        // A " " may be a false positive if they are defined as delimiters AND
1653        // are used to separate delimited queries
1654        var nDelimIndex = -1;
1655        for(var i = aDelimChar.length-1; i >= 0; i--) {
1656            var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]);
1657            if(nNewIndex > nDelimIndex) {
1658                nDelimIndex = nNewIndex;
1659            }
1660        }
1661        // If we think the last delimiter is a space (" "), make sure it is NOT
1662        // a false positive by also checking the char directly before it
1663        if(aDelimChar[i] == " ") {
1664            for (var j = aDelimChar.length-1; j >= 0; j--) {
1665                if(sQuery[nDelimIndex - 1] == aDelimChar[j]) {
1666                    nDelimIndex--;
1667                    break;
1668                }
1669            }
1670        }
1671        // A delimiter has been found in the query so extract the latest query from past selections
1672        if(nDelimIndex > -1) {
1673            var nQueryStart = nDelimIndex + 1;
1674            // Trim any white space from the beginning...
1675            while(sQuery.charAt(nQueryStart) == " ") {
1676                nQueryStart += 1;
1677            }
1678            // ...and save the rest of the string for later
1679            this._sPastSelections = sQuery.substring(0,nQueryStart);
1680            // Here is the query itself
1681            sQuery = sQuery.substr(nQueryStart);
1682        }
1683        // No delimiter found in the query, so there are no selections from past queries
1684        else {
1685            this._sPastSelections = "";
1686        }
1687    }
1688
1689    // Don't search queries that are too short
1690    if((sQuery && (sQuery.length < this.minQueryLength)) || (!sQuery && this.minQueryLength > 0)) {
1691        if(this._nDelayID != -1) {
1692            clearTimeout(this._nDelayID);
1693        }
1694        this._toggleContainer(false);
1695        return;
1696    }
1697
1698    sQuery = encodeURIComponent(sQuery);
1699    this._nDelayID = -1;    // Reset timeout ID because request is being made
1700    
1701    // Subset matching
1702    if(this.dataSource.queryMatchSubset || this.queryMatchSubset) { // backward compat
1703        var oResponse = this.getSubsetMatches(sQuery);
1704        if(oResponse) {
1705            this.handleResponse(sQuery, oResponse, {query: sQuery});
1706            return;
1707        }
1708    }
1709    
1710    if(this.responseStripAfter) {
1711        this.dataSource.doBeforeParseData = this.preparseRawResponse;
1712    }
1713    if(this.applyLocalFilter) {
1714        this.dataSource.doBeforeCallback = this.filterResults;
1715    }
1716    
1717    var sRequest = this.generateRequest(sQuery);
1718    this.dataRequestEvent.fire(this, sQuery, sRequest);
1719
1720    this.dataSource.sendRequest(sRequest, {
1721            success : this.handleResponse,
1722            failure : this.handleResponse,
1723            scope   : this,
1724            argument: {
1725                query: sQuery
1726            }
1727    });
1728};
1729
1730/**
1731 * Populates the array of &lt;li&gt; elements in the container with query
1732 * results.
1733 *
1734 * @method _populateList
1735 * @param sQuery {String} Original request.
1736 * @param oResponse {Object} Response object.
1737 * @param oPayload {MIXED} (optional) Additional argument(s)
1738 * @private
1739 */
1740YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, oResponse, oPayload) {
1741    // Clear previous timeout
1742    if(this._nTypeAheadDelayID != -1) {
1743        clearTimeout(this._nTypeAheadDelayID);
1744    }
1745        
1746    sQuery = (oPayload && oPayload.query) ? oPayload.query : sQuery;
1747    
1748    // Pass data through abstract method for any transformations
1749    var ok = this.doBeforeLoadData(sQuery, oResponse, oPayload);
1750
1751    // Data is ok
1752    if(ok && !oResponse.error) {
1753        this.dataReturnEvent.fire(this, sQuery, oResponse.results);
1754        
1755        // Continue only if instance is still focused (i.e., user hasn't already moved on)
1756        // Null indicates initialized state, which is ok too
1757        if(this._bFocused || (this._bFocused === null)) {
1758            
1759            //TODO: is this still necessary?
1760            /*var isOpera = (YAHOO.env.ua.opera);
1761            var contentStyle = this._elContent.style;
1762            contentStyle.width = (!isOpera) ? null : "";
1763            contentStyle.height = (!isOpera) ? null : "";*/
1764        
1765            // Store state for this interaction
1766            var sCurQuery = decodeURIComponent(sQuery);
1767            this._sCurQuery = sCurQuery;
1768            this._bItemSelected = false;
1769        
1770            var allResults = oResponse.results,
1771                nItemsToShow = Math.min(allResults.length,this.maxResultsDisplayed),
1772                sMatchKey = (this.dataSource.responseSchema.fields) ? 
1773                    (this.dataSource.responseSchema.fields[0].key || this.dataSource.responseSchema.fields[0]) : 0;
1774            
1775            if(nItemsToShow > 0) {
1776                // Make sure container and helpers are ready to go
1777                if(!this._elList || (this._elList.childNodes.length < nItemsToShow)) {
1778                    this._initListEl();
1779                }
1780                this._initContainerHelperEls();
1781                
1782                var allListItemEls = this._elList.childNodes;
1783                // Fill items with data from the bottom up
1784                for(var i = nItemsToShow-1; i >= 0; i--) {
1785                    var elListItem = allListItemEls[i],
1786                    oResult = allResults[i];
1787                    
1788                    // Backward compatibility
1789                    if(this.resultTypeList) {
1790                        // Results need to be converted back to an array
1791                        var aResult = [];
1792                        // Match key is first
1793                        aResult[0] = (YAHOO.lang.isString(oResult)) ? oResult : oResult[sMatchKey] || oResult[this.key];
1794                        // Add additional data to the result array
1795                        var fields = this.dataSource.responseSchema.fields;
1796                        if(YAHOO.lang.isArray(fields) && (fields.length > 1)) {
1797                            for(var k=1, len=fields.length; k<len; k++) {
1798                                aResult[aResult.length] = oResult[fields[k].key || fields[k]];
1799                            }
1800                        }
1801                        // No specific fields defined, so pass along entire data object
1802                        else {
1803                            // Already an array
1804                            if(YAHOO.lang.isArray(oResult)) {
1805                                aResult = oResult;
1806                            }
1807                            // Simple string 
1808                            else if(YAHOO.lang.isString(oResult)) {
1809                                aResult = [oResult];
1810                            }
1811                            // Object
1812                            else {
1813                                aResult[1] = oResult;
1814                            }
1815                        }
1816                        oResult = aResult;
1817                    }
1818
1819                    // The matching value, including backward compatibility for array format and safety net
1820                    elListItem._sResultMatch = (YAHOO.lang.isString(oResult)) ? oResult : (YAHOO.lang.isArray(oResult)) ? oResult[0] : (oResult[sM
1820atchKey] || "");
1821                    elListItem._oResultData = oResult; // Additional data
1822                    elListItem.innerHTML = this.formatResult(oResult, sCurQuery, elListItem._sResultMatch);
1823                    elListItem.style.display = "";
1824                }
1825        
1826                // Clear out extraneous items
1827                if(nItemsToShow < allListItemEls.length) {
1828                    var extraListItem;
1829                    for(var j = allListItemEls.length-1; j >= nItemsToShow; j--) {
1830                        extraListItem = allListItemEls[j];
1831                        extraListItem.style.display = "none";
1832                    }
1833                }
1834                
1835                this._nDisplayedItems = nItemsToShow;
1836                
1837                this.containerPopulateEvent.fire(this, sQuery, allResults);
1838                
1839                // Highlight the first item
1840                if(this.autoHighlight) {
1841                    var elFirstListItem = this._elList.firstChild;
1842                    this._toggleHighlight(elFirstListItem,"to");
1843                    this.itemArrowToEvent.fire(this, elFirstListItem);
1844                    this._typeAhead(elFirstListItem,sQuery);
1845                }
1846                // Unhighlight any previous time
1847                else {
1848                    this._toggleHighlight(this._elCurListItem,"from");
1849                }
1850        
1851                // Expand the container
1852                ok = this.doBeforeExpandContainer(this._elTextbox, this._elContainer, sQuery, allResults);
1853                this._toggleContainer(ok);
1854            }
1855            else {
1856                this._toggleContainer(false);
1857            }
1858
1859            return;
1860        }
1861    }
1862    // Error
1863    else {
1864        this.dataErrorEvent.fire(this, sQuery);
1865    }
1866        
1867};
1868
1869/**
1870 * When forceSelection is true and the user attempts
1871 * leave the text input box without selecting an item from the query results,
1872 * the user selection is cleared.
1873 *
1874 * @method _clearSelection
1875 * @private
1876 */
1877YAHOO.widget.AutoComplete.prototype._clearSelection = function() {
1878    var sValue = this._elTextbox.value;
1879    //TODO: need to check against all delimChars?
1880    var sChar = (this.delimChar) ? this.delimChar[0] : null;
1881    var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1;
1882    if(nIndex > -1) {
1883        this._elTextbox.value = sValue.substring(0,nIndex);
1884    }
1885    else {
1886         this._elTextbox.value = "";
1887    }
1888    this._sPastSelections = this._elTextbox.value;
1889
1890    // Fire custom event
1891    this.selectionEnforceEvent.fire(this);
1892};
1893
1894/**
1895 * Whether or not user-typed value in the text input box matches any of the
1896 * query results.
1897 *
1898 * @method _textMatchesOption
1899 * @return {HTMLElement} Matching list item element if user-input text matches
1900 * a result, null otherwise.
1901 * @private
1902 */
1903YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() {
1904    var elMatch = null;
1905
1906    for(var i = this._nDisplayedItems-1; i >= 0 ; i--) {
1907        var elListItem = this._elList.childNodes[i];
1908        var sMatch = ("" + elListItem._sResultMatch).toLowerCase();
1909        if(sMatch == this._sCurQuery.toLowerCase()) {
1910            elMatch = elListItem;
1911            break;
1912        }
1913    }
1914    return(elMatch);
1915};
1916
1917/**
1918 * Updates in the text input box with the first query result as the user types,
1919 * selecting the substring that the user has not typed.
1920 *
1921 * @method _typeAhead
1922 * @param elListItem {HTMLElement} The &lt;li&gt; element item whose data populates the input field.
1923 * @param sQuery {String} Query string.
1924 * @private
1925 */
1926YAHOO.widget.AutoComplete.prototype._typeAhead = function(elListItem, sQuery) {
1927    // Don't typeAhead if turned off or is backspace
1928    if(!this.typeAhead || (this._nKeyCode == 8)) {
1929        return;
1930    }
1931
1932    var oSelf = this,
1933        elTextbox = this._elTextbox;
1934        
1935    // Only if text selection is supported
1936    if(elTextbox.setSelectionRange || elTextbox.createTextRange) {
1937        // Set and store timeout for this typeahead
1938        this._nTypeAheadDelayID = setTimeout(function() {
1939                // Select the portion of text that the user has not typed
1940                var nStart = elTextbox.value.length; // any saved queries plus what user has typed
1941                oSelf._updateValue(elListItem);
1942                var nEnd = elTextbox.value.length;
1943                oSelf._selectText(elTextbox,nStart,nEnd);
1944                var sPrefill = elTextbox.value.substr(nStart,nEnd);
1945                oSelf.typeAheadEvent.fire(oSelf,sQuery,sPrefill);
1946            },(this.typeAheadDelay*1000));            
1947    }
1948};
1949
1950/**
1951 * Selects text in the input field.
1952 *
1953 * @method _selectText
1954 * @param elTextbox {HTMLElement} Text input box element in which to select text.
1955 * @param nStart {Number} Starting index of text string to select.
1956 * @param nEnd {Number} Ending index of text selection.
1957 * @private
1958 */
1959YAHOO.widget.AutoComplete.prototype._selectText = function(elTextbox, nStart, nEnd) {
1960    if(elTextbox.setSelectionRange) { // For Mozilla
1961        elTextbox.setSelectionRange(nStart,nEnd);
1962    }
1963    else if(elTextbox.createTextRange) { // For IE
1964        var oTextRange = elTextbox.createTextRange();
1965        oTextRange.moveStart("character", nStart);
1966        oTextRange.moveEnd("character", nEnd-elTextbox.value.length);
1967        oTextRange.select();
1968    }
1969    else {
1970        elTextbox.select();
1971    }
1972};
1973
1974/**
1975 * Syncs results container with its helpers.
1976 *
1977 * @method _toggleContainerHelpers
1978 * @param bShow {Boolean} True if container is expanded, false if collapsed
1979 * @private
1980 */
1981YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) {
1982    var width = this._elContent.offsetWidth + "px";
1983    var height = this._elContent.offsetHeight + "px";
1984
1985    if(this.useIFrame && this._elIFrame) {
1986    var elIFrame = this._elIFrame;
1987        if(bShow) {
1988            elIFrame.style.width = width;
1989            elIFrame.style.height = height;
1990            elIFrame.style.padding = "";
1991        }
1992        else {
1993            elIFrame.style.width = 0;
1994            elIFrame.style.height = 0;
1995            elIFrame.style.padding = 0;
1996        }
1997    }
1998    if(this.useShadow && this._elShadow) {
1999    var elShadow = this._elShadow;
2000        if(bShow) {
2001            elShadow.style.width = width;
2002            elShadow.style.height = height;
2003        }
2004        else {
2005            elShadow.style.width = 0;
2006            elShadow.style.height = 0;
2007        }
2008    }
2009};
2010
2011/**
2012 * Animates expansion or collapse of the container.
2013 *
2014 * @method _toggleContainer
2015 * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed
2016 * @private
2017 */
2018YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) {
2019    var elContainer = this._elContainer;
2020
2021    // If implementer has container always open and it's already open, don't mess with it
2022    // Container is initialized with display "none" so it may need to be shown first time through
2023    if(this.alwaysShowContainer && this._bContainerOpen) {
2024        return;
2025    }
2026    
2027    // Reset states
2028    if(!bShow) {
2029        this._toggleHighlight(this._elCurListItem,"from");
2030        this._nDisplayedItems = 0;
2031        this._sCurQuery = null;
2032        
2033        // Container is already closed, so don't bother with changing the UI
2034        if(!this._bContainerOpen) {
2035            this._elContent.style.display = "none";
2036            return;
2037        }
2038    }
2039
2040    // If animation is enabled...
2041    var oAnim = this._oAnim;
2042    if(oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) {
2043        if(oAnim.isAnimated()) {
2044            oAnim.stop(true);
2045        }
2046
2047        // Clone container to grab current size offscreen
2048        var oClone = this._elContent.cloneNode(true);
2049        elContainer.appendChild(oClone);
2050        oClone.style.top = "-9000px";
2051        oClone.style.width = "";
2052        oClone.style.height = "";
2053        oClone.style.display = "";
2054
2055        // Current size of the container is the EXPANDED size
2056        var wExp = oClone.offsetWidth;
2057        var hExp = oClone.offsetHeight;
2058
2059        // Calculate COLLAPSED sizes based on horiz and vert anim
2060        var wColl = (this.animHoriz) ? 0 : wExp;
2061        var hColl = (this.animVert) ? 0 : hExp;
2062
2063        // Set animation sizes
2064        oAnim.attributes = (bShow) ?
2065            {width: { to: wExp }, height: { to: hExp }} :
2066            {width: { to: wColl}, height: { to: hColl }};
2067
2068        // If opening anew, set to a collapsed size...
2069        if(bShow && !this._bContainerOpen) {
2070            this._elContent.style.width = wColl+"px";
2071            this._elContent.style.height = hColl+"px";
2072        }
2073        // Else, set it to its last known size.
2074        else {
2075            this._elContent.style.width = wExp+"px";
2076            this._elContent.style.height = hExp+"px";
2077        }
2078
2079        elContainer.removeChild(oClone);
2080        oClone = null;
2081
2082    	var oSelf = this;
2083    	var onAnimComplete = function() {
2084            // Finish the collapse
2085    		oAnim.onComplete.unsubscribeAll();
2086
2087            if(bShow) {
2088                oSelf._toggleContainerHelpers(true);
2089                oSelf._bContainerOpen = bShow;
2090                oSelf.containerExpandEvent.fire(oSelf);
2091            }
2092            else {
2093                oSelf._elContent.style.display = "none";
2094                oSelf._bContainerOpen = bShow;
2095                oSelf.containerCollapseEvent.fire(oSelf);
2096            }
2097     	};
2098
2099        // Display container and animate it
2100        this._toggleContainerHelpers(false); // Bug 1424486: Be early to hide, late to show;
2101        this._elContent.style.display = "";
2102        oAnim.onComplete.subscribe(onAnimComplete);
2103        oAnim.animate();
2104    }
2105    // Else don't animate, just show or hide
2106    else {
2107        if(bShow) {
2108            this._elContent.style.display = "";
2109            this._toggleContainerHelpers(true);
2110            this._bContainerOpen = bShow;
2111            this.containerExpandEvent.fire(this);
2112        }
2113        else {
2114            this._toggleContainerHelpers(false);
2115            this._elContent.style.display = "none";
2116            this._bContainerOpen = bShow;
2117            this.containerCollapseEvent.fire(this);
2118        }
2119   }
2120
2121};
2122
2123/**
2124 * Toggles the highlight on or off for an item in the container, and also cleans
2125 * up highlighting of any previous item.
2126 *
2127 * @method _toggleHighlight
2128 * @param elNewListItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
2129 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
2130 * @private
2131 */
2132YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(elNewListItem, sType) {
2133    if(elNewListItem) {
2134        var sHighlight = this.highlightClassName;
2135        if(this._elCurListItem) {
2136            // Remove highlight from old item
2137            YAHOO.util.Dom.removeClass(this._elCurListItem, sHighlight);
2138            this._elCurListItem = null;
2139        }
2140    
2141        if((sType == "to") && sHighlight) {
2142            // Apply highlight to new item
2143            YAHOO.util.Dom.addClass(elNewListItem, sHighlight);
2144            this._elCurListItem = elNewListItem;
2145        }
2146    }
2147};
2148
2149/**
2150 * Toggles the pre-highlight on or off for an item in the container.
2151 *
2152 * @method _togglePrehighlight
2153 * @param elNewListItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
2154 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
2155 * @private
2156 */
2157YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(elNewListItem, sType) {
2158    if(elNewListItem == this._elCurListItem) {
2159        return;
2160    }
2161
2162    var sPrehighlight = this.prehighlightClassName;
2163    if((sType == "mouseover") && sPrehighlight) {
2164        // Apply prehighlight to new item
2165        YAHOO.util.Dom.addClass(elNewListItem, sPrehighlight);
2166    }
2167    else {
2168        // Remove prehighlight from old item
2169        YAHOO.util.Dom.removeClass(elNewListItem, sPrehighlight);
2170    }
2171};
2172
2173/**
2174 * Updates the text input box value with selected query result. If a delimiter
2175 * has been defined, then the value gets appended with the delimiter.
2176 *
2177 * @method _updateValue
2178 * @param elListItem {HTMLElement} The &lt;li&gt; element item with which to update the value.
2179 * @private
2180 */
2181YAHOO.widget.AutoComplete.prototype._updateValue = function(elListItem) {
2182    if(!this.suppressInputUpdate) {    
2183        var elTextbox = this._elTextbox;
2184        var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null;
2185        var sResultMatch = elListItem._sResultMatch;
2186    
2187        // Calculate the new value
2188        var sNewValue = "";
2189        if(sDelimChar) {
2190            // Preserve selections from past queries
2191            sNewValue = this._sPastSelections;
2192            // Add new selection plus delimiter
2193            sNewValue += sResultMatch + sDelimChar;
2194            if(sDelimChar != " ") {
2195                sNewValue += " ";
2196            }
2197        }
2198        else { 
2199            sNewValue = sResultMatch;
2200        }
2201        
2202        // Update input field
2203        elTextbox.value = sNewValue;
2204    
2205        // Scroll to bottom of textarea if necessary
2206        if(elTextbox.type == "textarea") {
2207            elTextbox.scrollTop = elTextbox.scrollHeight;
2208        }
2209    
2210        // Move cursor to end
2211        var end = elTextbox.value.length;
2212        this._selectText(elTextbox,end,end);
2213    
2214        this._elCurListItem = elListItem;
2215    }
2216};
2217
2218/**
2219 * Selects a result item from the container
2220 *
2221 * @method _selectItem
2222 * @param elListItem {HTMLElement} The selected &lt;li&gt; element item.
2223 * @private
2224 */
2225YAHOO.widget.AutoComplete.prototype._selectItem = function(elListItem) {
2226    this._bItemSelected = true;
2227    this._updateValue(elListItem);
2228    this._sPastSelections = this._elTextbox.value;
2229    this._clearInterval();
2230    this.itemSelectEvent.fire(this, elListItem, elListItem._oResultData);
2231    this._toggleContainer(false);
2232};
2233
2234/**
2235 * If an item is highlighted in the container, the right arrow key jumps to the
2236 * end of the textbox and selects the highlighted item, otherwise the container
2237 * is closed.
2238 *
2239 * @method _jumpSelection
2240 * @private
2241 */
2242YAHOO.widget.AutoComplete.prototype._jumpSelection = function() {
2243    if(this._elCurListItem) {
2244        this._selectItem(this._elCurListItem);
2245    }
2246    else {
2247        this._toggleContainer(false);
2248    }
2249};
2250
2251/**
2252 * Triggered by up and down arrow keys, changes the current highlighted
2253 * &lt;li&gt; element item. Scrolls container if necessary.
2254 *
2255 * @method _moveSelection
2256 * @param nKeyCode {Number} Code of key pressed.
2257 * @private
2258 */
2259YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) {
2260    if(this._bContainerOpen) {
2261        // Determine current item's id number
2262        var elCurListItem = this._elCurListItem;
2263        var nCurItemIndex = -1;
2264
2265        if(elCurListItem) {
2266            nCurItemIndex = elCurListItem._nItemIndex;
2267        }
2268
2269        var nNewItemIndex = (nKeyCode == 40) ?
2270                (nCurItemIndex + 1) : (nCurItemIndex - 1);
2271
2272        // Out of bounds
2273        if(nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) {
2274            return;
2275        }
2276
2277        if(elCurListItem) {
2278            // Unhighlight current item
2279            this._toggleHighlight(elCurListItem, "from");
2280            this.itemArrowFromEvent.fire(this, elCurListItem);
2281        }
2282        if(nNewItemIndex == -1) {
2283           // Go back to query (remove type-ahead string)
2284            if(this.delimChar) {
2285                this._elTextbox.value = this._sPastSelections + this._sCurQuery;
2286            }
2287            else {
2288                this._elTextbox.value = this._sCurQuery;
2289            }
2290            return;
2291        }
2292        if(nNewItemIndex == -2) {
2293            // Close container
2294            this._toggleContainer(false);
2295            return;
2296        }
2297        
2298        var elNewListItem = this._elList.childNodes[nNewItemIndex];
2299
2300        // Scroll the container if necessary
2301        var elContent = this._elContent;
2302        var scrollOn = ((YAHOO.util.Dom.getStyle(elContent,"overflow") == "auto") ||
2303            (YAHOO.util.Dom.getStyle(elContent,"overflowY") == "auto"));
2304        if(scrollOn && (nNewItemIndex > -1) &&
2305        (nNewItemIndex < this._nDisplayedItems)) {
2306            // User is keying down
2307            if(nKeyCode == 40) {
2308                // Bottom of selected item is below scroll area...
2309                if((elNewListItem.offsetTop+elNewListItem.offsetHeight) > (elContent.scrollTop + elContent.offsetHeight)) {
2310                    // Set bottom of scroll area to bottom of selected item
2311                    elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight;
2312                }
2313                // Bottom of selected item is above scroll area...
2314                else if((elNewListItem.offsetTop+elNewListItem.offsetHeight) < elContent.scrollTop) {
2315                    // Set top of selected item to top of scroll area
2316                    elContent.scrollTop = elNewListItem.offsetTop;
2317
2318                }
2319            }
2320            // User is keying up
2321            else {
2322                // Top of selected item is above scroll area
2323                if(elNewListItem.offsetTop < elContent.scrollTop) {
2324                    // Set top of scroll area to top of selected item
2325                    this._elContent.scrollTop = elNewListItem.offsetTop;
2326                }
2327                // Top of selected item is below scroll area
2328                else if(elNewListItem.offsetTop > (elContent.scrollTop + elContent.offsetHeight)) {
2329                    // Set bottom of selected item to bottom of scroll area
2330                    this._elContent.scrollTop = (elNewListItem.offsetTop+elNewListItem.offsetHeight) - elContent.offsetHeight;
2331                }
2332            }
2333        }
2334
2335        this._toggleHighlight(elNewListItem, "to");
2336        this.itemArrowToEvent.fire(this, elNewListItem);
2337        if(this.typeAhead) {
2338            this._updateValue(elNewListItem);
2339        }
2340    }
2341};
2342
2343/////////////////////////////////////////////////////////////////////////////
2344//
2345// Private event handlers
2346//
2347/////////////////////////////////////////////////////////////////////////////
2348
2349/**
2350 * Handles &lt;li&gt; element mouseover events in the container.
2351 *
2352 * @method _onItemMouseover
2353 * @param v {HTMLEvent} The mouseover event.
2354 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2355 * @private
2356 */
2357/*YAHOO.widget.AutoComplete.prototype._onItemMouseover = function(v,oSelf) {
2358    if(oSelf.prehighlightClassName) {
2359        oSelf._togglePrehighlight(this,"mouseover");
2360    }
2361    else {
2362        oSelf._toggleHighlight(this,"to");
2363    }
2364
2365    oSelf.itemMouseOverEvent.fire(oSelf, this);
2366};*/
2367
2368/**
2369 * Handles &lt;li&gt; element mouseout events in the container.
2370 *
2371 * @method _onItemMouseout
2372 * @param v {HTMLEvent} The mouseout event.
2373 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2374 * @private
2375 */
2376/*YAHOO.widget.AutoComplete.prototype._onItemMouseout = function(v,oSelf) {
2377    if(oSelf.prehighlightClassName) {
2378        oSelf._togglePrehighlight(this,"mouseout");
2379    }
2380    else {
2381        oSelf._toggleHighlight(this,"from");
2382    }
2383
2384    oSelf.itemMouseOutEvent.fire(oSelf, this);
2385};*/
2386
2387/**
2388 * Handles &lt;li&gt; element click events in the container.
2389 *
2390 * @method _onItemMouseclick
2391 * @param v {HTMLEvent} The click event.
2392 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2393 * @private
2394 */
2395/*YAHOO.widget.AutoComplete.prototype._onItemMouseclick = function(v,oSelf) {
2396    // In case item has not been moused over
2397    oSelf._toggleHighlight(this,"to");
2398    oSelf._selectItem(this);
2399};*/
2400
2401/**
2402 * Handles container mouseover events.
2403 *
2404 * @method _onContainerMouseover
2405 * @param v {HTMLEvent} The mouseover event.
2406 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2407 * @private
2408 */
2409YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) {
2410    var elTarget = YAHOO.util.Event.getTarget(v);
2411    var elTag = elTarget.nodeName.toLowerCase();
2412    while(elTarget && (elTag != "table")) {
2413        switch(elTag) {
2414            case "body":
2415                return;
2416            case "li":
2417                if(oSelf.prehighlightClassName) {
2418                    oSelf._togglePrehighlight(elTarget,"mouseover");
2419                }
2420                else {
2421                    oSelf._toggleHighlight(elTarget,"to");
2422                }
2423            
2424                oSelf.itemMouseOverEvent.fire(oSelf, elTarget);
2425                break;
2426            case "div":
2427                if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) {
2428                    oSelf._bOverContainer = true;
2429                    return;
2430                }
2431                break;
2432            default:
2433                break;
2434        }
2435        
2436        elTarget = elTarget.parentNode;
2437        if(elTarget) {
2438            elTag = elTarget.nodeName.toLowerCase();
2439        }
2440    }
2441};
2442
2443/**
2444 * Handles container mouseout events.
2445 *
2446 * @method _onContainerMouseout
2447 * @param v {HTMLEvent} The mouseout event.
2448 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2449 * @private
2450 */
2451YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) {
2452    var elTarget = YAHOO.util.Event.getTarget(v);
2453    var elTag = elTarget.nodeName.toLowerCase();
2454    while(elTarget && (elTag != "table")) {
2455        switch(elTag) {
2456            case "body":
2457                return;
2458            case "li":
2459                if(oSelf.prehighlightClassName) {
2460                    oSelf._togglePrehighlight(elTarget,"mouseout");
2461                }
2462                else {
2463                    oSelf._toggleHighlight(elTarget,"from");
2464                }
2465            
2466                oSelf.itemMouseOutEvent.fire(oSelf, elTarget);
2467                break;
2468            case "ul":
2469                oSelf._toggleHighlight(oSelf._elCurListItem,"to");
2470                break;
2471            case "div":
2472                if(YAHOO.util.Dom.hasClass(elTarget,"yui-ac-container")) {
2473                    oSelf._bOverContainer = false;
2474                    return;
2475                }
2476                break;
2477            default:
2478                break;
2479        }
2480
2481        elTarget = elTarget.parentNode;
2482        if(elTarget) {
2483            elTag = elTarget.nodeName.toLowerCase();
2484        }
2485    }
2486};
2487
2488/**
2489 * Handles container click events.
2490 *
2491 * @method _onContainerClick
2492 * @param v {HTMLEvent} The click event.
2493 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2494 * @private
2495 */
2496YAHOO.widget.AutoComplete.prototype._onContainerClick = function(v,oSelf) {
2497    var elTarget = YAHOO.util.Event.getTarget(v);
2498    var elTag = elTarget.nodeName.toLowerCase();
2499    while(elTarget && (elTag != "table")) {
2500        switch(elTag) {
2501            case "body":
2502                return;
2503            case "li":
2504                // In case item has not been moused over
2505                oSelf._toggleHighlight(elTarget,"to");
2506                oSelf._selectItem(elTarget);
2507                return;
2508            default:
2509                break;
2510        }
2511
2512        elTarget = elTarget.parentNode;
2513        if(elTarget) {
2514            elTag = elTarget.nodeName.toLowerCase();
2515        }
2516    }    
2517};
2518
2519
2520/**
2521 * Handles container scroll events.
2522 *
2523 * @method _onContainerScroll
2524 * @param v {HTMLEvent} The scroll event.
2525 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2526 * @private
2527 */
2528YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) {
2529    oSelf._elTextbox.focus();
2530};
2531
2532/**
2533 * Handles container resize events.
2534 *
2535 * @method _onContainerResize
2536 * @param v {HTMLEvent} The resize event.
2537 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2538 * @private
2539 */
2540YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) {
2541    oSelf._toggleContainerHelpers(oSelf._bContainerOpen);
2542};
2543
2544
2545/**
2546 * Handles textbox keydown events of functional keys, mainly for UI behavior.
2547 *
2548 * @method _onTextboxKeyDown
2549 * @param v {HTMLEvent} The keydown event.
2550 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2551 * @private
2552 */
2553YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) {
2554    var nKeyCode = v.keyCode;
2555
2556    // Clear timeout
2557    if(oSelf._nTypeAheadDelayID != -1) {
2558        clearTimeout(oSelf._nTypeAheadDelayID);
2559    }
2560    
2561    switch (nKeyCode) {
2562        case 9: // tab
2563            if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) {
2564                // select an item or clear out
2565                if(oSelf._elCurListItem) {
2566                    if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {
2567                        if(oSelf._bContainerOpen) {
2568                            YAHOO.util.Event.stopEvent(v);
2569                        }
2570                    }
2571                    oSelf._selectItem(oSelf._elCurListItem);
2572                }
2573                else {
2574                    oSelf._toggleContainer(false);
2575                }
2576            }
2577            break;
2578        case 13: // enter
2579            if(!YAHOO.env.ua.opera && (navigator.userAgent.toLowerCase().indexOf("mac") == -1) || (YAHOO.env.ua.webkit>420)) {
2580                if(oSelf._elCurListItem) {
2581                    if(oSelf._nKeyCode != nKeyCode) {
2582                        if(oSelf._bContainerOpen) {
2583                            YAHOO.util.Event.stopEvent(v);
2584                        }
2585                    }
2586                    oSelf._selectItem(oSelf._elCurListItem);
2587                }
2588                else {
2589                    oSelf._toggleContainer(false);
2590                }
2591            }
2592            break;
2593        case 27: // esc
2594            oSelf._toggleContainer(false);
2595            return;
2596        case 39: // right
2597            oSelf._jumpSelection();
2598            break;
2599        case 38: // up
2600            if(oSelf._bContainerOpen) {
2601                YAHOO.util.Event.stopEvent(v);
2602                oSelf._moveSelection(nKeyCode);
2603            }
2604            break;
2605        case 40: // down
2606            if(oSelf._bContainerOpen) {
2607                YAHOO.util.Event.stopEvent(v);
2608                oSelf._moveSelection(nKeyCode);
2609            }
2610            break;
2611        default: 
2612            oSelf._bItemSelected = false;
2613            oSelf._toggleHighlight(oSelf._elCurListItem, "from");
2614
2615            oSelf.textboxKeyEvent.fire(oSelf, nKeyCode);
2616            break;
2617    }
2618
2619    if(nKeyCode === 18){
2620        oSelf._enableIntervalDetection();
2621    }    
2622    oSelf._nKeyCode = nKeyCode;
2623};
2624
2625/**
2626 * Handles textbox keypress events.
2627 * @method _onTextboxKeyPress
2628 * @param v {HTMLEvent} The keypress event.
2629 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2630 * @private
2631 */
2632YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) {
2633    var nKeyCode = v.keyCode;
2634
2635        // Expose only to non SF3 (bug 1978549) Mac browsers (bug 790337) and  Opera browsers (bug 583531),
2636        // where stopEvent is ineffective on keydown events 
2637        if(YAHOO.env.ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") != -1) && (YAHOO.env.ua.webkit < 420)) {
2638            switch (nKeyCode) {
2639            case 9: // tab
2640                // select an item or clear out
2641                if(oSelf._bContainerOpen) {
2642                    if(oSelf.delimChar) {
2643                        YAHOO.util.Event.stopEvent(v);
2644                    }
2645                    if(oSelf._elCurListItem) {
2646                        oSelf._selectItem(oSelf._elCurListItem);
2647                    }
2648                    else {
2649                        oSelf._toggleContainer(false);
2650                    }
2651                }
2652                break;
2653            case 13: // enter
2654                if(oSelf._bContainerOpen) {
2655                    YAHOO.util.Event.stopEvent(v);
2656                    if(oSelf._elCurListItem) {
2657                        oSelf._selectItem(oSelf._elCurListItem);
2658                    }
2659                    else {
2660                        oSelf._toggleContainer(false);
2661                    }
2662                }
2663                break;
2664            default:
2665                break;
2666            }
2667        }
2668
2669        //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948)
2670        // Korean IME detected
2671        else if(nKeyCode == 229) {
2672            oSelf._enableIntervalDetection();
2673        }
2674};
2675
2676/**
2677 * Handles textbox keyup events to trigger queries.
2678 *
2679 * @method _onTextboxKeyUp
2680 * @param v {HTMLEvent} The keyup event.
2681 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2682 * @private
2683 */
2684YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) {
2685    var sText = this.value; //string in textbox
2686    
2687    // Check to see if any of the public properties have been updated
2688    oSelf._initProps();
2689
2690    // Filter out chars that don't trigger queries
2691    var nKeyCode = v.keyCode;
2692    if(oSelf._isIgnoreKey(nKeyCode)) {
2693        return;
2694    }
2695
2696    // Clear previous timeout
2697    /*if(oSelf._nTypeAheadDelayID != -1) {
2698        clearTimeout(oSelf._nTypeAheadDelayID);
2699    }*/
2700    if(oSelf._nDelayID != -1) {
2701        clearTimeout(oSelf._nDelayID);
2702    }
2703
2704    // Set new timeout
2705    oSelf._nDelayID = setTimeout(function(){
2706            oSelf._sendQuery(sText);
2707        },(oSelf.queryDelay * 1000));
2708
2709     //= nDelayID;
2710    //else {
2711        // No delay so send request immediately
2712        //oSelf._sendQuery(sText);
2713   //}
2714};
2715
2716/**
2717 * Handles text input box receiving focus.
2718 *
2719 * @method _onTextboxFocus
2720 * @param v {HTMLEvent} The focus event.
2721 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2722 * @private
2723 */
2724YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) {
2725    // Start of a new interaction
2726    if(!oSelf._bFocused) {
2727        oSelf._elTextbox.setAttribute("autocomplete","off");
2728        oSelf._bFocused = true;
2729        oSelf._sInitInputValue = oSelf._elTextbox.value;
2730        oSelf.textboxFocusEvent.fire(oSelf);
2731    }
2732};
2733
2734/**
2735 * Handles text input box losing focus.
2736 *
2737 * @method _onTextboxBlur
2738 * @param v {HTMLEvent} The focus event.
2739 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2740 * @private
2741 */
2742YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) {
2743    // Don't treat as a blur if it was a selection via mouse click
2744    if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) {
2745        // Current query needs to be validated as a selection
2746        if(!oSelf._bItemSelected) {
2747            var elMatchListItem = oSelf._textMatchesOption();
2748            // Container is closed or current query doesn't match any result
2749            if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && (elMatchListItem === null))) {
2750                // Force selection is enabled so clear the current query
2751                if(oSelf.forceSelection) {
2752                    oSelf._clearSelection();
2753                }
2754                // Treat current query as a valid selection
2755                else {
2756                    oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery);
2757                }
2758            }
2759            // Container is open and current query matches a result
2760            else {
2761                // Force a selection when textbox is blurred with a match
2762                if(oSelf.forceSelection) {
2763                    oSelf._selectItem(elMatchListItem);
2764                }
2765            }
2766        }
2767
2768        if(oSelf._bContainerOpen) {
2769            oSelf._toggleContainer(false);
2770        }
2771        oSelf._clearInterval();
2772        oSelf._bFocused = false;
2773        if(oSelf._sInitInputValue !== oSelf._elTextbox.value) {
2774            oSelf.textboxChangeEvent.fire(oSelf);
2775        }
2776        oSelf.textboxBlurEvent.fire(oSelf);
2777    }
2778};
2779
2780/**
2781 * Handles window unload event.
2782 *
2783 * @method _onWindowUnload
2784 * @param v {HTMLEvent} The unload event.
2785 * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
2786 * @private
2787 */
2788YAHOO.widget.AutoComplete.prototype._onWindowUnload = function(v,oSelf) {
2789    if(oSelf && oSelf._elTextbox && oSelf.allowBrowserAutocomplete) {
2790        oSelf._elTextbox.setAttribute("autocomplete","on");
2791    }
2792};
2793
2794/////////////////////////////////////////////////////////////////////////////
2795//
2796// Deprecated for Backwards Compatibility
2797//
2798/////////////////////////////////////////////////////////////////////////////
2799/**
2800 * @method doBeforeSendQuery
2801 * @deprecated Use generateRequest.
2802 */
2803YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery = function(sQuery) {
2804    return this.generateRequest(sQuery);
2805};
2806
2807/**
2808 * @method getListItems
2809 * @deprecated Use getListEl().childNodes.
2810 */
2811YAHOO.widget.AutoComplete.prototype.getListItems = function() {
2812    var allListItemEls = [],
2813        els = this._elList.childNodes;
2814    for(var i=els.length-1; i>=0; i--) {
2815        allListItemEls[i] = els[i];
2816    }
2817    return allListItemEls;
2818};
2819
2820/////////////////////////////////////////////////////////////////////////
2821//
2822// Private static methods
2823//
2824/////////////////////////////////////////////////////////////////////////
2825
2826/**
2827 * Clones object literal or array of object literals.
2828 *
2829 * @method AutoComplete._cloneObject
2830 * @param o {Object} Object.
2831 * @private
2832 * @static     
2833 */
2834YAHOO.widget.AutoComplete._cloneObject = function(o) {
2835    if(!YAHOO.lang.isValue(o)) {
2836        return o;
2837    }
2838    
2839    var copy = {};
2840    
2841    if(YAHOO.lang.isFunction(o)) {
2842        copy = o;
2843    }
2844    else if(YAHOO.lang.isArray(o)) {
2845        var array = [];
2846        for(var i=0,len=o.length;i<len;i++) {
2847            array[i] = YAHOO.widget.AutoComplete._cloneObject(o[i]);
2848        }
2849        copy = array;
2850    }
2851    else if(YAHOO.lang.isObject(o)) { 
2852        for (var x in o){
2853            if(YAHOO.lang.hasOwnProperty(o, x)) {
2854                if(YAHOO.lang.isValue(o[x]) && YAHOO.lang.isObject(o[x]) || YAHOO.lang.isArray(o[x])) {
2855                    copy[x] = YAHOO.widget.AutoComplete._cloneObject(o[x]);
2856                }
2857                else {
2858                    copy[x] = o[x];
2859                }
2860            }
2861        }
2862    }
2863    else {
2864        copy = o;
2865    }
2866
2867    return copy;
2868};
2869
2870
2871
2872
2873YAHOO.register("autocomplete", YAHOO.widget.AutoComplete, {version: "2.6.0", build: "1321"});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.