View | Details | Raw Unified | Return to bug 7747
Collapse All | Expand All

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