Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / lib / yui / autocomplete / autocomplete-debug.js
blob45dc20894d95a6074b071b1ca2aaad2fe2c31966
1 /*
2 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 0.12.2
6 */
7  /**\r
8  * The AutoComplete control provides the front-end logic for text-entry suggestion and\r
9  * completion functionality.\r
10  *\r
11  * @module autocomplete\r
12  * @requires yahoo, dom, event, datasource\r
13  * @optional animation, connection, json\r
14  * @namespace YAHOO.widget\r
15  * @title AutoComplete Widget\r
16  */\r
18 /****************************************************************************/\r
19 /****************************************************************************/\r
20 /****************************************************************************/\r
22 /**\r
23  * The AutoComplete class provides the customizable functionality of a plug-and-play DHTML\r
24  * auto completion widget.  Some key features:\r
25  * <ul>\r
26  * <li>Navigate with up/down arrow keys and/or mouse to pick a selection</li>\r
27  * <li>The drop down container can "roll down" or "fly out" via configurable\r
28  * animation</li>\r
29  * <li>UI look-and-feel customizable through CSS, including container\r
30  * attributes, borders, position, fonts, etc</li>\r
31  * </ul>\r
32  *\r
33  * @class AutoComplete\r
34  * @constructor\r
35  * @param elInput {HTMLElement} DOM element reference of an input field.\r
36  * @param elInput {String} String ID of an input field.\r
37  * @param elContainer {HTMLElement} DOM element reference of an existing DIV.\r
38  * @param elContainer {String} String ID of an existing DIV.\r
39  * @param oDataSource {Object} Instance of YAHOO.widget.DataSource for query/results.\r
40  * @param oConfigs {Object} (optional) Object literal of configuration params.\r
41  */\r
42 YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {\r
43     if(elInput && elContainer && oDataSource) {\r
44         // Validate DataSource\r
45         if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) {\r
46             this.dataSource = oDataSource;\r
47         }\r
48         else {\r
49             YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString());\r
50             return;\r
51         }\r
53         // Validate input element\r
54         if(YAHOO.util.Dom.inDocument(elInput)) {\r
55             if(typeof elInput == "string") {\r
56                     this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;\r
57                     this._oTextbox = document.getElementById(elInput);\r
58             }\r
59             else {\r
60                 this._sName = (elInput.id) ?\r
61                     "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:\r
62                     "instance" + YAHOO.widget.AutoComplete._nIndex;\r
63                 this._oTextbox = elInput;\r
64             }\r
65         }\r
66         else {\r
67             YAHOO.log("Could not instantiate AutoComplete due to an invalid input element", "error", this.toString());\r
68             return;\r
69         }\r
71         // Validate container element\r
72         if(YAHOO.util.Dom.inDocument(elContainer)) {\r
73             if(typeof elContainer == "string") {\r
74                     this._oContainer = document.getElementById(elContainer);\r
75             }\r
76             else {\r
77                 this._oContainer = elContainer;\r
78             }\r
79             if(this._oContainer.style.display == "none") {\r
80                 YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString());\r
81             }\r
82         }\r
83         else {\r
84             YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString());\r
85             return;\r
86         }\r
88         // Set any config params passed in to override defaults\r
89         if (typeof oConfigs == "object") {\r
90             for(var sConfig in oConfigs) {\r
91                 if (sConfig) {\r
92                     this[sConfig] = oConfigs[sConfig];\r
93                 }\r
94             }\r
95         }\r
97         // Initialization sequence\r
98         this._initContainer();\r
99         this._initProps();\r
100         this._initList();\r
101         this._initContainerHelpers();\r
103         // Set up events\r
104         var oSelf = this;\r
105         var oTextbox = this._oTextbox;\r
106         // Events are actually for the content module within the container\r
107         var oContent = this._oContainer._oContent;\r
109         // Dom events\r
110         YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);\r
111         YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);\r
112         YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);\r
113         YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);\r
114         YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);\r
115         YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);\r
116         YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);\r
117         YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);\r
118         if(oTextbox.form) {\r
119             YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);\r
120         }\r
121         YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);\r
123         // Custom events\r
124         this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);\r
125         this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);\r
126         this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);\r
127         this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);\r
128         this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);\r
129         this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);\r
130         this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);\r
131         this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);\r
132         this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);\r
133         this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);\r
134         this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);\r
135         this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);\r
136         this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);\r
137         this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);\r
138         this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);\r
139         this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);\r
140         \r
141         // Finish up\r
142         oTextbox.setAttribute("autocomplete","off");\r
143         YAHOO.widget.AutoComplete._nIndex++;\r
144         YAHOO.log("AutoComplete initialized","info",this.toString());\r
145     }\r
146     // Required arguments were not found\r
147     else {\r
148         YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString());\r
149     }\r
150 };\r
152 /////////////////////////////////////////////////////////////////////////////\r
153 //\r
154 // Public member variables\r
155 //\r
156 /////////////////////////////////////////////////////////////////////////////\r
158 /**\r
159  * The DataSource object that encapsulates the data used for auto completion.\r
160  * This object should be an inherited object from YAHOO.widget.DataSource.\r
161  *\r
162  * @property dataSource\r
163  * @type Object\r
164  */\r
165 YAHOO.widget.AutoComplete.prototype.dataSource = null;\r
167 /**\r
168  * Number of characters that must be entered before querying for results. A negative value\r
169  * effectively turns off the widget. A value of 0 allows queries of null or empty string\r
170  * values.\r
171  *\r
172  * @property minQueryLength\r
173  * @type Number\r
174  * @default 1\r
175  */\r
176 YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;\r
178 /**\r
179  * Maximum number of results to display in results container.\r
180  *\r
181  * @property maxResultsDisplayed\r
182  * @type Number\r
183  * @default 10\r
184  */\r
185 YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;\r
187 /**\r
188  * Number of seconds to delay before submitting a query request.  If a query\r
189  * request is received before a previous one has completed its delay, the\r
190  * previous request is cancelled and the new request is set to the delay.\r
191  *\r
192  * @property queryDelay\r
193  * @type Number\r
194  * @default 0.5\r
195  */\r
196 YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5;\r
198 /**\r
199  * Class name of a highlighted item within results container.\r
200  *\r
201  * @property highlighClassName\r
202  * @type String\r
203  * @default "yui-ac-highlight"\r
204  */\r
205 YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";\r
207 /**\r
208  * Class name of a pre-highlighted item within results container.\r
209  *\r
210  * @property prehighlightClassName\r
211  * @type String\r
212  */\r
213 YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;\r
215 /**\r
216  * Query delimiter. A single character separator for multiple delimited\r
217  * selections. Multiple delimiter characteres may be defined as an array of\r
218  * strings. A null value or empty string indicates that query results cannot\r
219  * be delimited. This feature is not recommended if you need forceSelection to\r
220  * be true.\r
221  *\r
222  * @property delimChar\r
223  * @type String | String[]\r
224  */\r
225 YAHOO.widget.AutoComplete.prototype.delimChar = null;\r
227 /**\r
228  * Whether or not the first item in results container should be automatically highlighted\r
229  * on expand.\r
230  *\r
231  * @property autoHighlight\r
232  * @type Boolean\r
233  * @default true\r
234  */\r
235 YAHOO.widget.AutoComplete.prototype.autoHighlight = true;\r
237 /**\r
238  * Whether or not the input field should be automatically updated\r
239  * with the first query result as the user types, auto-selecting the substring\r
240  * that the user has not typed.\r
241  *\r
242  * @property typeAhead\r
243  * @type Boolean\r
244  * @default false\r
245  */\r
246 YAHOO.widget.AutoComplete.prototype.typeAhead = false;\r
248 /**\r
249  * Whether or not to animate the expansion/collapse of the results container in the\r
250  * horizontal direction.\r
251  *\r
252  * @property animHoriz\r
253  * @type Boolean\r
254  * @default false\r
255  */\r
256 YAHOO.widget.AutoComplete.prototype.animHoriz = false;\r
258 /**\r
259  * Whether or not to animate the expansion/collapse of the results container in the\r
260  * vertical direction.\r
261  *\r
262  * @property animVert\r
263  * @type Boolean\r
264  * @default true\r
265  */\r
266 YAHOO.widget.AutoComplete.prototype.animVert = true;\r
268 /**\r
269  * Speed of container expand/collapse animation, in seconds..\r
270  *\r
271  * @property animSpeed\r
272  * @type Number\r
273  * @default 0.3\r
274  */\r
275 YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;\r
277 /**\r
278  * Whether or not to force the user's selection to match one of the query\r
279  * results. Enabling this feature essentially transforms the input field into a\r
280  * &lt;select&gt; field. This feature is not recommended with delimiter character(s)\r
281  * defined.\r
282  *\r
283  * @property forceSelection\r
284  * @type Boolean\r
285  * @default false\r
286  */\r
287 YAHOO.widget.AutoComplete.prototype.forceSelection = false;\r
289 /**\r
290  * Whether or not to allow browsers to cache user-typed input in the input\r
291  * field. Disabling this feature will prevent the widget from setting the\r
292  * autocomplete="off" on the input field. When autocomplete="off"\r
293  * and users click the back button after form submission, user-typed input can\r
294  * be prefilled by the browser from its cache. This caching of user input may\r
295  * not be desired for sensitive data, such as credit card numbers, in which\r
296  * case, implementers should consider setting allowBrowserAutocomplete to false.\r
297  *\r
298  * @property allowBrowserAutocomplete\r
299  * @type Boolean\r
300  * @default true\r
301  */\r
302 YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;\r
304 /**\r
305  * Whether or not the results container should always be displayed.\r
306  * Enabling this feature displays the container when the widget is instantiated\r
307  * and prevents the toggling of the container to a collapsed state.\r
308  *\r
309  * @property alwaysShowContainer\r
310  * @type Boolean\r
311  * @default false\r
312  */\r
313 YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;\r
315 /**\r
316  * Whether or not to use an iFrame to layer over Windows form elements in\r
317  * IE. Set to true only when the results container will be on top of a\r
318  * &lt;select&gt; field in IE and thus exposed to the IE z-index bug (i.e.,\r
319  * 5.5 < IE < 7).\r
320  *\r
321  * @property useIFrame\r
322  * @type Boolean\r
323  * @default false\r
324  */\r
325 YAHOO.widget.AutoComplete.prototype.useIFrame = false;\r
327 /**\r
328  * Whether or not the results container should have a shadow.\r
329  *\r
330  * @property useShadow\r
331  * @type Boolean\r
332  * @default false\r
333  */\r
334 YAHOO.widget.AutoComplete.prototype.useShadow = false;\r
336 /////////////////////////////////////////////////////////////////////////////\r
337 //\r
338 // Public methods\r
339 //\r
340 /////////////////////////////////////////////////////////////////////////////\r
342  /**\r
343  * Public accessor to the unique name of the AutoComplete instance.\r
344  *\r
345  * @method toString\r
346  * @return {String} Unique name of the AutoComplete instance.\r
347  */\r
348 YAHOO.widget.AutoComplete.prototype.toString = function() {\r
349     return "AutoComplete " + this._sName;\r
350 };\r
352  /**\r
353  * Returns true if container is in an expanded state, false otherwise.\r
354  *\r
355  * @method isContainerOpen\r
356  * @return {Boolean} Returns true if container is in an expanded state, false otherwise.\r
357  */\r
358 YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {\r
359     return this._bContainerOpen;\r
360 };\r
362 /**\r
363  * Public accessor to the internal array of DOM &lt;li&gt; elements that\r
364  * display query results within the results container.\r
365  *\r
366  * @method getListItems\r
367  * @return {HTMLElement[]} Array of &lt;li&gt; elements within the results container.\r
368  */\r
369 YAHOO.widget.AutoComplete.prototype.getListItems = function() {\r
370     return this._aListItems;\r
371 };\r
373 /**\r
374  * Public accessor to the data held in an &lt;li&gt; element of the\r
375  * results container.\r
376  *\r
377  * @method getListItemData\r
378  * @return {Object | Array} Object or array of result data or null\r
379  */\r
380 YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {\r
381     if(oListItem._oResultData) {\r
382         return oListItem._oResultData;\r
383     }\r
384     else {\r
385         return false;\r
386     }\r
387 };\r
389 /**\r
390  * Sets HTML markup for the results container header. This markup will be\r
391  * inserted within a &lt;div&gt; tag with a class of "ac_hd".\r
392  *\r
393  * @method setHeader\r
394  * @param sHeader {String} HTML markup for results container header.\r
395  */\r
396 YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {\r
397     if(sHeader) {\r
398         if(this._oContainer._oContent._oHeader) {\r
399             this._oContainer._oContent._oHeader.innerHTML = sHeader;\r
400             this._oContainer._oContent._oHeader.style.display = "block";\r
401         }\r
402     }\r
403     else {\r
404         this._oContainer._oContent._oHeader.innerHTML = "";\r
405         this._oContainer._oContent._oHeader.style.display = "none";\r
406     }\r
407 };\r
409 /**\r
410  * Sets HTML markup for the results container footer. This markup will be\r
411  * inserted within a &lt;div&gt; tag with a class of "ac_ft".\r
412  *\r
413  * @method setFooter\r
414  * @param sFooter {String} HTML markup for results container footer.\r
415  */\r
416 YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {\r
417     if(sFooter) {\r
418         if(this._oContainer._oContent._oFooter) {\r
419             this._oContainer._oContent._oFooter.innerHTML = sFooter;\r
420             this._oContainer._oContent._oFooter.style.display = "block";\r
421         }\r
422     }\r
423     else {\r
424         this._oContainer._oContent._oFooter.innerHTML = "";\r
425         this._oContainer._oContent._oFooter.style.display = "none";\r
426     }\r
427 };\r
429 /**\r
430  * Sets HTML markup for the results container body. This markup will be\r
431  * inserted within a &lt;div&gt; tag with a class of "ac_bd".\r
432  *\r
433  * @method setBody\r
434  * @param sHeader {String} HTML markup for results container body.\r
435  */\r
436 YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {\r
437     if(sBody) {\r
438         if(this._oContainer._oContent._oBody) {\r
439             this._oContainer._oContent._oBody.innerHTML = sBody;\r
440             this._oContainer._oContent._oBody.style.display = "block";\r
441             this._oContainer._oContent.style.display = "block";\r
442         }\r
443     }\r
444     else {\r
445         this._oContainer._oContent._oBody.innerHTML = "";\r
446         this._oContainer._oContent.style.display = "none";\r
447     }\r
448     this._maxResultsDisplayed = 0;\r
449 };\r
451 /**\r
452  * Overridable method that converts a result item object into HTML markup\r
453  * for display. Return data values are accessible via the oResultItem object,\r
454  * and the key return value will always be oResultItem[0]. Markup will be\r
455  * displayed within &lt;li&gt; element tags in the container.\r
456  *\r
457  * @method formatResult\r
458  * @param oResultItem {Object} Result item representing one query result. Data is held in an array.\r
459  * @param sQuery {String} The current query string.\r
460  * @return {String} HTML markup of formatted result data.\r
461  */\r
462 YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {\r
463     var sResult = oResultItem[0];\r
464     if(sResult) {\r
465         return sResult;\r
466     }\r
467     else {\r
468         return "";\r
469     }\r
470 };\r
472 /**\r
473  * Overridable method called before container expands allows implementers to access data\r
474  * and DOM elements.\r
475  *\r
476  * @method doBeforeExpandContainer\r
477  * @return {Boolean} Return true to continue expanding container, false to cancel the expand.\r
478  */\r
479 YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oResultItem, sQuery) {\r
480     return true;\r
481 };\r
483 /**\r
484  * Makes query request to the DataSource.\r
485  *\r
486  * @method sendQuery\r
487  * @param sQuery {String} Query string.\r
488  */\r
489 YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {\r
490     this._sendQuery(sQuery);\r
491 };\r
493 /////////////////////////////////////////////////////////////////////////////\r
494 //\r
495 // Public events\r
496 //\r
497 /////////////////////////////////////////////////////////////////////////////\r
499 /**\r
500  * Fired when the input field receives focus.\r
501  *\r
502  * @event textboxFocusEvent\r
503  * @param oSelf {Object} The AutoComplete instance.\r
504  */\r
505 YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;\r
507 /**\r
508  * Fired when the input field receives key input.\r
509  *\r
510  * @event textboxKeyEvent\r
511  * @param oSelf {Object} The AutoComplete instance.\r
512  * @param nKeycode {Number} The keycode number.\r
513  */\r
514 YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;\r
516 /**\r
517  * Fired when the AutoComplete instance makes a query to the DataSource.\r
518  * \r
519  * @event dataRequestEvent\r
520  * @param oSelf {Object} The AutoComplete instance.\r
521  * @param sQuery {String} The query string.\r
522  */\r
523 YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;\r
525 /**\r
526  * Fired when the AutoComplete instance receives query results from the data\r
527  * source.\r
528  *\r
529  * @event dataReturnEvent\r
530  * @param oSelf {Object} The AutoComplete instance.\r
531  * @param sQuery {String} The query string.\r
532  * @param aResults {Array} Results array.\r
533  */\r
534 YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;\r
536 /**\r
537  * Fired when the AutoComplete instance does not receive query results from the\r
538  * DataSource due to an error.\r
539  *\r
540  * @event dataErrorEvent\r
541  * @param oSelf {Object} The AutoComplete instance.\r
542  * @param sQuery {String} The query string.\r
543  */\r
544 YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;\r
546 /**\r
547  * Fired when the results container is expanded.\r
548  *\r
549  * @event containerExpandEvent\r
550  * @param oSelf {Object} The AutoComplete instance.\r
551  */\r
552 YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;\r
554 /**\r
555  * Fired when the input field has been prefilled by the type-ahead\r
556  * feature. \r
557  *\r
558  * @event typeAheadEvent\r
559  * @param oSelf {Object} The AutoComplete instance.\r
560  * @param sQuery {String} The query string.\r
561  * @param sPrefill {String} The prefill string.\r
562  */\r
563 YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;\r
565 /**\r
566  * Fired when result item has been moused over.\r
567  *\r
568  * @event itemMouseOverEvent\r
569  * @param oSelf {Object} The AutoComplete instance.\r
570  * @param elItem {HTMLElement} The &lt;li&gt element item moused to.\r
571  */\r
572 YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;\r
574 /**\r
575  * Fired when result item has been moused out.\r
576  *\r
577  * @event itemMouseOutEvent\r
578  * @param oSelf {Object} The AutoComplete instance.\r
579  * @param elItem {HTMLElement} The &lt;li&gt; element item moused from.\r
580  */\r
581 YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;\r
583 /**\r
584  * Fired when result item has been arrowed to. \r
585  *\r
586  * @event itemArrowToEvent\r
587  * @param oSelf {Object} The AutoComplete instance.\r
588  * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed to.\r
589  */\r
590 YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;\r
592 /**\r
593  * Fired when result item has been arrowed away from.\r
594  *\r
595  * @event itemArrowFromEvent\r
596  * @param oSelf {Object} The AutoComplete instance.\r
597  * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed from.\r
598  */\r
599 YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;\r
601 /**\r
602  * Fired when an item is selected via mouse click, ENTER key, or TAB key.\r
603  *\r
604  * @event itemSelectEvent\r
605  * @param oSelf {Object} The AutoComplete instance.\r
606  * @param elItem {HTMLElement} The selected &lt;li&gt; element item.\r
607  * @param oData {Object} The data returned for the item, either as an object,\r
608  * or mapped from the schema into an array.\r
609  */\r
610 YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;\r
612 /**\r
613  * Fired when a user selection does not match any of the displayed result items.\r
614  * Note that this event may not behave as expected when delimiter characters\r
615  * have been defined. \r
616  *\r
617  * @event unmatchedItemSelectEvent\r
618  * @param oSelf {Object} The AutoComplete instance.\r
619  * @param sQuery {String} The user-typed query string.\r
620  */\r
621 YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;\r
623 /**\r
624  * Fired if forceSelection is enabled and the user's input has been cleared\r
625  * because it did not match one of the returned query results.\r
626  *\r
627  * @event selectionEnforceEvent\r
628  * @param oSelf {Object} The AutoComplete instance.\r
629  */\r
630 YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;\r
632 /**\r
633  * Fired when the results container is collapsed.\r
634  *\r
635  * @event containerCollapseEvent\r
636  * @param oSelf {Object} The AutoComplete instance.\r
637  */\r
638 YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;\r
640 /**\r
641  * Fired when the input field loses focus.\r
642  *\r
643  * @event textboxBlurEvent\r
644  * @param oSelf {Object} The AutoComplete instance.\r
645  */\r
646 YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;\r
648 /////////////////////////////////////////////////////////////////////////////\r
649 //\r
650 // Private member variables\r
651 //\r
652 /////////////////////////////////////////////////////////////////////////////\r
654 /**\r
655  * Internal class variable to index multiple AutoComplete instances.\r
656  *\r
657  * @property _nIndex\r
658  * @type Number\r
659  * @default 0\r
660  * @private\r
661  */\r
662 YAHOO.widget.AutoComplete._nIndex = 0;\r
664 /**\r
665  * Name of AutoComplete instance.\r
666  *\r
667  * @property _sName\r
668  * @type String\r
669  * @private\r
670  */\r
671 YAHOO.widget.AutoComplete.prototype._sName = null;\r
673 /**\r
674  * Text input field DOM element.\r
675  *\r
676  * @property _oTextbox\r
677  * @type HTMLElement\r
678  * @private\r
679  */\r
680 YAHOO.widget.AutoComplete.prototype._oTextbox = null;\r
682 /**\r
683  * Whether or not the input field is currently in focus. If query results come back\r
684  * but the user has already moved on, do not proceed with auto complete behavior.\r
685  *\r
686  * @property _bFocused\r
687  * @type Boolean\r
688  * @private\r
689  */\r
690 YAHOO.widget.AutoComplete.prototype._bFocused = true;\r
692 /**\r
693  * Animation instance for container expand/collapse.\r
694  *\r
695  * @property _oAnim\r
696  * @type Boolean\r
697  * @private\r
698  */\r
699 YAHOO.widget.AutoComplete.prototype._oAnim = null;\r
701 /**\r
702  * Container DOM element.\r
703  *\r
704  * @property _oContainer\r
705  * @type HTMLElement\r
706  * @private\r
707  */\r
708 YAHOO.widget.AutoComplete.prototype._oContainer = null;\r
710 /**\r
711  * Whether or not the results container is currently open.\r
712  *\r
713  * @property _bContainerOpen\r
714  * @type Boolean\r
715  * @private\r
716  */\r
717 YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;\r
719 /**\r
720  * Whether or not the mouse is currently over the results\r
721  * container. This is necessary in order to prevent clicks on container items\r
722  * from being text input field blur events.\r
723  *\r
724  * @property _bOverContainer\r
725  * @type Boolean\r
726  * @private\r
727  */\r
728 YAHOO.widget.AutoComplete.prototype._bOverContainer = false;\r
730 /**\r
731  * Array of &lt;li&gt; elements references that contain query results within the\r
732  * results container.\r
733  *\r
734  * @property _aListItems\r
735  * @type Array\r
736  * @private\r
737  */\r
738 YAHOO.widget.AutoComplete.prototype._aListItems = null;\r
740 /**\r
741  * Number of &lt;li&gt; elements currently displayed in results container.\r
742  *\r
743  * @property _nDisplayedItems\r
744  * @type Number\r
745  * @private\r
746  */\r
747 YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;\r
749 /**\r
750  * Internal count of &lt;li&gt; elements displayed and hidden in results container.\r
751  *\r
752  * @property _maxResultsDisplayed\r
753  * @type Number\r
754  * @private\r
755  */\r
756 YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;\r
758 /**\r
759  * Current query string\r
760  *\r
761  * @property _sCurQuery\r
762  * @type String\r
763  * @private\r
764  */\r
765 YAHOO.widget.AutoComplete.prototype._sCurQuery = null;\r
767 /**\r
768  * Past queries this session (for saving delimited queries).\r
769  *\r
770  * @property _sSavedQuery\r
771  * @type String\r
772  * @private\r
773  */\r
774 YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;\r
776 /**\r
777  * Pointer to the currently highlighted &lt;li&gt; element in the container.\r
778  *\r
779  * @property _oCurItem\r
780  * @type HTMLElement\r
781  * @private\r
782  */\r
783 YAHOO.widget.AutoComplete.prototype._oCurItem = null;\r
785 /**\r
786  * Whether or not an item has been selected since the container was populated\r
787  * with results. Reset to false by _populateList, and set to true when item is\r
788  * selected.\r
789  *\r
790  * @property _bItemSelected\r
791  * @type Boolean\r
792  * @private\r
793  */\r
794 YAHOO.widget.AutoComplete.prototype._bItemSelected = false;\r
796 /**\r
797  * Key code of the last key pressed in textbox.\r
798  *\r
799  * @property _nKeyCode\r
800  * @type Number\r
801  * @private\r
802  */\r
803 YAHOO.widget.AutoComplete.prototype._nKeyCode = null;\r
805 /**\r
806  * Delay timeout ID.\r
807  *\r
808  * @property _nDelayID\r
809  * @type Number\r
810  * @private\r
811  */\r
812 YAHOO.widget.AutoComplete.prototype._nDelayID = -1;\r
814 /**\r
815  * Src to iFrame used when useIFrame = true. Supports implementations over SSL\r
816  * as well.\r
817  *\r
818  * @property _iFrameSrc\r
819  * @type String\r
820  * @private\r
821  */\r
822 YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";\r
824 /**\r
825  * For users typing via certain IMEs, queries must be triggered by intervals,\r
826  * since key events yet supported across all browsers for all IMEs.\r
827  *\r
828  * @property _queryInterval\r
829  * @type Object\r
830  * @private\r
831  */\r
832 YAHOO.widget.AutoComplete.prototype._queryInterval = null;\r
834 /**\r
835  * Internal tracker to last known textbox value, used to determine whether or not\r
836  * to trigger a query via interval for certain IME users.\r
837  *\r
838  * @event _sLastTextboxValue\r
839  * @type String\r
840  * @private\r
841  */\r
842 YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;\r
844 /////////////////////////////////////////////////////////////////////////////\r
845 //\r
846 // Private methods\r
847 //\r
848 /////////////////////////////////////////////////////////////////////////////\r
850 /**\r
851  * Updates and validates latest public config properties.\r
852  *\r
853  * @method __initProps\r
854  * @private\r
855  */\r
856 YAHOO.widget.AutoComplete.prototype._initProps = function() {\r
857     // Correct any invalid values\r
858     var minQueryLength = this.minQueryLength;\r
859     if(isNaN(minQueryLength) || (minQueryLength < 1)) {\r
860         minQueryLength = 1;\r
861     }\r
862     var maxResultsDisplayed = this.maxResultsDisplayed;\r
863     if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) {\r
864         this.maxResultsDisplayed = 10;\r
865     }\r
866     var queryDelay = this.queryDelay;\r
867     if(isNaN(this.queryDelay) || (this.queryDelay < 0)) {\r
868         this.queryDelay = 0.5;\r
869     }\r
870     var aDelimChar = (this.delimChar) ? this.delimChar : null;\r
871     if(aDelimChar) {\r
872         if(typeof aDelimChar == "string") {\r
873             this.delimChar = [aDelimChar];\r
874         }\r
875         else if(aDelimChar.constructor != Array) {\r
876             this.delimChar = null;\r
877         }\r
878     }\r
879     var animSpeed = this.animSpeed;\r
880     if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {\r
881         if(isNaN(animSpeed) || (animSpeed < 0)) {\r
882             animSpeed = 0.3;\r
883         }\r
884         if(!this._oAnim ) {\r
885             oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);\r
886             this._oAnim = oAnim;\r
887         }\r
888         else {\r
889             this._oAnim.duration = animSpeed;\r
890         }\r
891     }\r
892     if(this.forceSelection && this.delimChar) {\r
893         YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString());\r
894     }\r
895 };\r
897 /**\r
898  * Initializes the results container helpers if they are enabled and do\r
899  * not exist\r
900  *\r
901  * @method _initContainerHelpers\r
902  * @private\r
903  */\r
904 YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {\r
905     if(this.useShadow && !this._oContainer._oShadow) {\r
906         var oShadow = document.createElement("div");\r
907         oShadow.className = "yui-ac-shadow";\r
908         this._oContainer._oShadow = this._oContainer.appendChild(oShadow);\r
909     }\r
910     if(this.useIFrame && !this._oContainer._oIFrame) {\r
911         var oIFrame = document.createElement("iframe");\r
912         oIFrame.src = this._iFrameSrc;\r
913         oIFrame.frameBorder = 0;\r
914         oIFrame.scrolling = "no";\r
915         oIFrame.style.position = "absolute";\r
916         oIFrame.style.width = "100%";\r
917         oIFrame.style.height = "100%";\r
918         oIFrame.tabIndex = -1;\r
919         this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);\r
920     }\r
921 };\r
923 /**\r
924  * Initializes the results container once at object creation\r
925  *\r
926  * @method _initContainer\r
927  * @private\r
928  */\r
929 YAHOO.widget.AutoComplete.prototype._initContainer = function() {\r
930     if(!this._oContainer._oContent) {\r
931         // The oContent div helps size the iframe and shadow properly\r
932         var oContent = document.createElement("div");\r
933         oContent.className = "yui-ac-content";\r
934         oContent.style.display = "none";\r
935         this._oContainer._oContent = this._oContainer.appendChild(oContent);\r
937         var oHeader = document.createElement("div");\r
938         oHeader.className = "yui-ac-hd";\r
939         oHeader.style.display = "none";\r
940         this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);\r
942         var oBody = document.createElement("div");\r
943         oBody.className = "yui-ac-bd";\r
944         this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);\r
946         var oFooter = document.createElement("div");\r
947         oFooter.className = "yui-ac-ft";\r
948         oFooter.style.display = "none";\r
949         this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);\r
950     }\r
951     else {\r
952         YAHOO.log("Could not initialize the container","warn",this.toString());\r
953     }\r
954 };\r
956 /**\r
957  * Clears out contents of container body and creates up to\r
958  * YAHOO.widget.AutoComplete#maxResultsDisplayed &lt;li&gt; elements in an\r
959  * &lt;ul&gt; element.\r
960  *\r
961  * @method _initList\r
962  * @private\r
963  */\r
964 YAHOO.widget.AutoComplete.prototype._initList = function() {\r
965     this._aListItems = [];\r
966     while(this._oContainer._oContent._oBody.hasChildNodes()) {\r
967         var oldListItems = this.getListItems();\r
968         if(oldListItems) {\r
969             for(var oldi = oldListItems.length-1; oldi >= 0; i--) {\r
970                 oldListItems[oldi] = null;\r
971             }\r
972         }\r
973         this._oContainer._oContent._oBody.innerHTML = "";\r
974     }\r
976     var oList = document.createElement("ul");\r
977     oList = this._oContainer._oContent._oBody.appendChild(oList);\r
978     for(var i=0; i<this.maxResultsDisplayed; i++) {\r
979         var oItem = document.createElement("li");\r
980         oItem = oList.appendChild(oItem);\r
981         this._aListItems[i] = oItem;\r
982         this._initListItem(oItem, i);\r
983     }\r
984     this._maxResultsDisplayed = this.maxResultsDisplayed;\r
985 };\r
987 /**\r
988  * Initializes each &lt;li&gt; element in the container list.\r
989  *\r
990  * @method _initListItem\r
991  * @param oItem {HTMLElement} The &lt;li&gt; DOM element.\r
992  * @param nItemIndex {Number} The index of the element.\r
993  * @private\r
994  */\r
995 YAHOO.widget.AutoComplete.prototype._initListItem = function(oItem, nItemIndex) {\r
996     var oSelf = this;\r
997     oItem.style.display = "none";\r
998     oItem._nItemIndex = nItemIndex;\r
1000     oItem.mouseover = oItem.mouseout = oItem.onclick = null;\r
1001     YAHOO.util.Event.addListener(oItem,"mouseover",oSelf._onItemMouseover,oSelf);\r
1002     YAHOO.util.Event.addListener(oItem,"mouseout",oSelf._onItemMouseout,oSelf);\r
1003     YAHOO.util.Event.addListener(oItem,"click",oSelf._onItemMouseclick,oSelf);\r
1004 };\r
1006 /**\r
1007  * Enables interval detection for  Korean IME support.\r
1008  *\r
1009  * @method _onIMEDetected\r
1010  * @param oSelf {Object} The AutoComplete instance.\r
1011  * @private\r
1012  */\r
1013 YAHOO.widget.AutoComplete.prototype._onIMEDetected = function(oSelf) {\r
1014     oSelf._enableIntervalDetection();\r
1015 };\r
1017 /**\r
1018  * Enables query triggers based on text input detection by intervals (rather\r
1019  * than by key events).\r
1020  *\r
1021  * @method _enableIntervalDetection\r
1022  * @private\r
1023  */\r
1024 YAHOO.widget.AutoComplete.prototype._enableIntervalDetection = function() {\r
1025     var currValue = this._oTextbox.value;\r
1026     var lastValue = this._sLastTextboxValue;\r
1027     if(currValue != lastValue) {\r
1028         this._sLastTextboxValue = currValue;\r
1029         this._sendQuery(currValue);\r
1030     }\r
1031 };\r
1034 /**\r
1035  * Cancels text input detection by intervals.\r
1036  *\r
1037  * @method _cancelIntervalDetection\r
1038  * @param oSelf {Object} The AutoComplete instance.\r
1039  * @private\r
1040  */\r
1041 YAHOO.widget.AutoComplete.prototype._cancelIntervalDetection = function(oSelf) {\r
1042     if(oSelf._queryInterval) {\r
1043         clearInterval(oSelf._queryInterval);\r
1044     }\r
1045 };\r
1048 /**\r
1049  * Whether or not key is functional or should be ignored. Note that the right\r
1050  * arrow key is NOT an ignored key since it triggers queries for certain intl\r
1051  * charsets.\r
1052  *\r
1053  * @method _isIgnoreKey\r
1054  * @param nKeycode {Number} Code of key pressed.\r
1055  * @return {Boolean} True if key should be ignored, false otherwise.\r
1056  * @private\r
1057  */\r
1058 YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) {\r
1059     if ((nKeyCode == 9) || (nKeyCode == 13)  || // tab, enter\r
1060             (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl\r
1061             (nKeyCode >= 18 && nKeyCode <= 20) || // alt,pause/break,caps lock\r
1062             (nKeyCode == 27) || // esc\r
1063             (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end\r
1064             (nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up\r
1065             (nKeyCode == 40) || // down\r
1066             (nKeyCode >= 44 && nKeyCode <= 45)) { // print screen,insert\r
1067         return true;\r
1068     }\r
1069     return false;\r
1070 };\r
1072 /**\r
1073  * Makes query request to the DataSource.\r
1074  *\r
1075  * @method _sendQuery\r
1076  * @param sQuery {String} Query string.\r
1077  * @private\r
1078  */\r
1079 YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) {\r
1080     // Widget has been effectively turned off\r
1081     if(this.minQueryLength == -1) {\r
1082         this._toggleContainer(false);\r
1083         return;\r
1084     }\r
1085     // Delimiter has been enabled\r
1086     var aDelimChar = (this.delimChar) ? this.delimChar : null;\r
1087     if(aDelimChar) {\r
1088         // Loop through all possible delimiters and find the latest one\r
1089         // A " " may be a false positive if they are defined as delimiters AND\r
1090         // are used to separate delimited queries\r
1091         var nDelimIndex = -1;\r
1092         for(var i = aDelimChar.length-1; i >= 0; i--) {\r
1093             var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]);\r
1094             if(nNewIndex > nDelimIndex) {\r
1095                 nDelimIndex = nNewIndex;\r
1096             }\r
1097         }\r
1098         // If we think the last delimiter is a space (" "), make sure it is NOT\r
1099         // a false positive by also checking the char directly before it\r
1100         if(aDelimChar[i] == " ") {\r
1101             for (var j = aDelimChar.length-1; j >= 0; j--) {\r
1102                 if(sQuery[nDelimIndex - 1] == aDelimChar[j]) {\r
1103                     nDelimIndex--;\r
1104                     break;\r
1105                 }\r
1106             }\r
1107         }\r
1108         // A delimiter has been found so extract the latest query\r
1109         if (nDelimIndex > -1) {\r
1110             var nQueryStart = nDelimIndex + 1;\r
1111             // Trim any white space from the beginning...\r
1112             while(sQuery.charAt(nQueryStart) == " ") {\r
1113                 nQueryStart += 1;\r
1114             }\r
1115             // ...and save the rest of the string for later\r
1116             this._sSavedQuery = sQuery.substring(0,nQueryStart);\r
1117             // Here is the query itself\r
1118             sQuery = sQuery.substr(nQueryStart);\r
1119         }\r
1120         else if(sQuery.indexOf(this._sSavedQuery) < 0){\r
1121             this._sSavedQuery = null;\r
1122         }\r
1123     }\r
1125     // Don't search queries that are too short\r
1126     if (sQuery && (sQuery.length < this.minQueryLength) || (!sQuery && this.minQueryLength > 0)) {\r
1127         if (this._nDelayID != -1) {\r
1128             clearTimeout(this._nDelayID);\r
1129         }\r
1130         this._toggleContainer(false);\r
1131         return;\r
1132     }\r
1134     sQuery = encodeURIComponent(sQuery);\r
1135     this._nDelayID = -1;    // Reset timeout ID because request has been made\r
1136     this.dataRequestEvent.fire(this, sQuery);\r
1137     this.dataSource.getResults(this._populateList, sQuery, this);\r
1138 };\r
1140 /**\r
1141  * Populates the array of &lt;li&gt; elements in the container with query\r
1142  * results. This method is passed to YAHOO.widget.DataSource#getResults as a\r
1143  * callback function so results from the DataSource instance are returned to the\r
1144  * AutoComplete instance.\r
1145  *\r
1146  * @method _populateList\r
1147  * @param sQuery {String} The query string.\r
1148  * @param aResults {Array} An array of query result objects from the DataSource.\r
1149  * @param oSelf {Object} The AutoComplete instance.\r
1150  * @private\r
1151  */\r
1152 YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, aResults, oSelf) {\r
1153     if(aResults === null) {\r
1154         oSelf.dataErrorEvent.fire(oSelf, sQuery);\r
1155     }\r
1156     if (!oSelf._bFocused || !aResults) {\r
1157         return;\r
1158     }\r
1160     var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);\r
1161     var contentStyle = oSelf._oContainer._oContent.style;\r
1162     contentStyle.width = (!isOpera) ? null : "";\r
1163     contentStyle.height = (!isOpera) ? null : "";\r
1165     var sCurQuery = decodeURIComponent(sQuery);\r
1166     oSelf._sCurQuery = sCurQuery;\r
1167     oSelf._bItemSelected = false;\r
1169     if(oSelf._maxResultsDisplayed != oSelf.maxResultsDisplayed) {\r
1170         oSelf._initList();\r
1171     }\r
1173     var nItems = Math.min(aResults.length,oSelf.maxResultsDisplayed);\r
1174     oSelf._nDisplayedItems = nItems;\r
1175     if (nItems > 0) {\r
1176         oSelf._initContainerHelpers();\r
1177         var aItems = oSelf._aListItems;\r
1179         // Fill items with data\r
1180         for(var i = nItems-1; i >= 0; i--) {\r
1181             var oItemi = aItems[i];\r
1182             var oResultItemi = aResults[i];\r
1183             oItemi.innerHTML = oSelf.formatResult(oResultItemi, sCurQuery);\r
1184             oItemi.style.display = "list-item";\r
1185             oItemi._sResultKey = oResultItemi[0];\r
1186             oItemi._oResultData = oResultItemi;\r
1188         }\r
1190         // Empty out remaining items if any\r
1191         for(var j = aItems.length-1; j >= nItems ; j--) {\r
1192             var oItemj = aItems[j];\r
1193             oItemj.innerHTML = null;\r
1194             oItemj.style.display = "none";\r
1195             oItemj._sResultKey = null;\r
1196             oItemj._oResultData = null;\r
1197         }\r
1199         if(oSelf.autoHighlight) {\r
1200             // Go to the first item\r
1201             var oFirstItem = aItems[0];\r
1202             oSelf._toggleHighlight(oFirstItem,"to");\r
1203             oSelf.itemArrowToEvent.fire(oSelf, oFirstItem);\r
1204             oSelf._typeAhead(oFirstItem,sQuery);\r
1205         }\r
1206         else {\r
1207             oSelf._oCurItem = null;\r
1208         }\r
1210         // Expand the container\r
1211         var ok = oSelf.doBeforeExpandContainer(oSelf._oTextbox, oSelf._oContainer, sQuery, aResults);\r
1212         oSelf._toggleContainer(ok);\r
1213     }\r
1214     else {\r
1215         oSelf._toggleContainer(false);\r
1216     }\r
1217     oSelf.dataReturnEvent.fire(oSelf, sQuery, aResults);\r
1218 };\r
1220 /**\r
1221  * When forceSelection is true and the user attempts\r
1222  * leave the text input box without selecting an item from the query results,\r
1223  * the user selection is cleared.\r
1224  *\r
1225  * @method _clearSelection\r
1226  * @private\r
1227  */\r
1228 YAHOO.widget.AutoComplete.prototype._clearSelection = function() {\r
1229     var sValue = this._oTextbox.value;\r
1230     var sChar = (this.delimChar) ? this.delimChar[0] : null;\r
1231     var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1;\r
1232     if(nIndex > -1) {\r
1233         this._oTextbox.value = sValue.substring(0,nIndex);\r
1234     }\r
1235     else {\r
1236          this._oTextbox.value = "";\r
1237     }\r
1238     this._sSavedQuery = this._oTextbox.value;\r
1240     // Fire custom event\r
1241     this.selectionEnforceEvent.fire(this);\r
1242 };\r
1244 /**\r
1245  * Whether or not user-typed value in the text input box matches any of the\r
1246  * query results.\r
1247  *\r
1248  * @method _textMatchesOption\r
1249  * @return {Boolean} True if user-input text matches a result, false otherwise.\r
1250  * @private\r
1251  */\r
1252 YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() {\r
1253     var foundMatch = false;\r
1255     for(var i = this._nDisplayedItems-1; i >= 0 ; i--) {\r
1256         var oItem = this._aListItems[i];\r
1257         var sMatch = oItem._sResultKey.toLowerCase();\r
1258         if (sMatch == this._sCurQuery.toLowerCase()) {\r
1259             foundMatch = true;\r
1260             break;\r
1261         }\r
1262     }\r
1263     return(foundMatch);\r
1264 };\r
1266 /**\r
1267  * Updates in the text input box with the first query result as the user types,\r
1268  * selecting the substring that the user has not typed.\r
1269  *\r
1270  * @method _typeAhead\r
1271  * @param oItem {HTMLElement} The &lt;li&gt; element item whose data populates the input field.\r
1272  * @param sQuery {String} Query string.\r
1273  * @private\r
1274  */\r
1275 YAHOO.widget.AutoComplete.prototype._typeAhead = function(oItem, sQuery) {\r
1276     // Don't update if turned off\r
1277     if (!this.typeAhead || (this._nKeyCode == 8)) {\r
1278         return;\r
1279     }\r
1281     var oTextbox = this._oTextbox;\r
1282     var sValue = this._oTextbox.value; // any saved queries plus what user has typed\r
1284     // Don't update with type-ahead if text selection is not supported\r
1285     if(!oTextbox.setSelectionRange && !oTextbox.createTextRange) {\r
1286         return;\r
1287     }\r
1289     // Select the portion of text that the user has not typed\r
1290     var nStart = sValue.length;\r
1291     this._updateValue(oItem);\r
1292     var nEnd = oTextbox.value.length;\r
1293     this._selectText(oTextbox,nStart,nEnd);\r
1294     var sPrefill = oTextbox.value.substr(nStart,nEnd);\r
1295     this.typeAheadEvent.fire(this,sQuery,sPrefill);\r
1296 };\r
1298 /**\r
1299  * Selects text in the input field.\r
1300  *\r
1301  * @method _selectText\r
1302  * @param oTextbox {HTMLElement} Text input box element in which to select text.\r
1303  * @param nStart {Number} Starting index of text string to select.\r
1304  * @param nEnd {Number} Ending index of text selection.\r
1305  * @private\r
1306  */\r
1307 YAHOO.widget.AutoComplete.prototype._selectText = function(oTextbox, nStart, nEnd) {\r
1308     if (oTextbox.setSelectionRange) { // For Mozilla\r
1309         oTextbox.setSelectionRange(nStart,nEnd);\r
1310     }\r
1311     else if (oTextbox.createTextRange) { // For IE\r
1312         var oTextRange = oTextbox.createTextRange();\r
1313         oTextRange.moveStart("character", nStart);\r
1314         oTextRange.moveEnd("character", nEnd-oTextbox.value.length);\r
1315         oTextRange.select();\r
1316     }\r
1317     else {\r
1318         oTextbox.select();\r
1319     }\r
1320 };\r
1322 /**\r
1323  * Syncs results container with its helpers.\r
1324  *\r
1325  * @method _toggleContainerHelpers\r
1326  * @param bShow {Boolean} True if container is expanded, false if collapsed\r
1327  * @private\r
1328  */\r
1329 YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) {\r
1330     var bFireEvent = false;\r
1331     var width = this._oContainer._oContent.offsetWidth + "px";\r
1332     var height = this._oContainer._oContent.offsetHeight + "px";\r
1334     if(this.useIFrame && this._oContainer._oIFrame) {\r
1335         bFireEvent = true;\r
1336         if(bShow) {\r
1337             this._oContainer._oIFrame.style.width = width;\r
1338             this._oContainer._oIFrame.style.height = height;\r
1339         }\r
1340         else {\r
1341             this._oContainer._oIFrame.style.width = 0;\r
1342             this._oContainer._oIFrame.style.height = 0;\r
1343         }\r
1344     }\r
1345     if(this.useShadow && this._oContainer._oShadow) {\r
1346         bFireEvent = true;\r
1347         if(bShow) {\r
1348             this._oContainer._oShadow.style.width = width;\r
1349             this._oContainer._oShadow.style.height = height;\r
1350         }\r
1351         else {\r
1352            this._oContainer._oShadow.style.width = 0;\r
1353             this._oContainer._oShadow.style.height = 0;\r
1354         }\r
1355     }\r
1356 };\r
1358 /**\r
1359  * Animates expansion or collapse of the container.\r
1360  *\r
1361  * @method _toggleContainer\r
1362  * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed\r
1363  * @private\r
1364  */\r
1365 YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) {\r
1366     var oContainer = this._oContainer;\r
1368     // Implementer has container always open so don't mess with it\r
1369     if(this.alwaysShowContainer && this._bContainerOpen) {\r
1370         return;\r
1371     }\r
1372     \r
1373     // Clear contents of container\r
1374     if(!bShow) {\r
1375         this._oContainer._oContent.scrollTop = 0;\r
1376         var aItems = this._aListItems;\r
1378         if(aItems && (aItems.length > 0)) {\r
1379             for(var i = aItems.length-1; i >= 0 ; i--) {\r
1380                 aItems[i].style.display = "none";\r
1381             }\r
1382         }\r
1384         if (this._oCurItem) {\r
1385             this._toggleHighlight(this._oCurItem,"from");\r
1386         }\r
1388         this._oCurItem = null;\r
1389         this._nDisplayedItems = 0;\r
1390         this._sCurQuery = null;\r
1391     }\r
1393     // Container is already closed\r
1394     if (!bShow && !this._bContainerOpen) {\r
1395         oContainer._oContent.style.display = "none";\r
1396         return;\r
1397     }\r
1399     // If animation is enabled...\r
1400     var oAnim = this._oAnim;\r
1401     if (oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) {\r
1402         // If helpers need to be collapsed, do it right away...\r
1403         // but if helpers need to be expanded, wait until after the container expands\r
1404         if(!bShow) {\r
1405             this._toggleContainerHelpers(bShow);\r
1406         }\r
1408         if(oAnim.isAnimated()) {\r
1409             oAnim.stop();\r
1410         }\r
1412         // Clone container to grab current size offscreen\r
1413         var oClone = oContainer._oContent.cloneNode(true);\r
1414         oContainer.appendChild(oClone);\r
1415         oClone.style.top = "-9000px";\r
1416         oClone.style.display = "block";\r
1418         // Current size of the container is the EXPANDED size\r
1419         var wExp = oClone.offsetWidth;\r
1420         var hExp = oClone.offsetHeight;\r
1422         // Calculate COLLAPSED sizes based on horiz and vert anim\r
1423         var wColl = (this.animHoriz) ? 0 : wExp;\r
1424         var hColl = (this.animVert) ? 0 : hExp;\r
1426         // Set animation sizes\r
1427         oAnim.attributes = (bShow) ?\r
1428             {width: { to: wExp }, height: { to: hExp }} :\r
1429             {width: { to: wColl}, height: { to: hColl }};\r
1431         // If opening anew, set to a collapsed size...\r
1432         if(bShow && !this._bContainerOpen) {\r
1433             oContainer._oContent.style.width = wColl+"px";\r
1434             oContainer._oContent.style.height = hColl+"px";\r
1435         }\r
1436         // Else, set it to its last known size.\r
1437         else {\r
1438             oContainer._oContent.style.width = wExp+"px";\r
1439             oContainer._oContent.style.height = hExp+"px";\r
1440         }\r
1442         oContainer.removeChild(oClone);\r
1443         oClone = null;\r
1445         var oSelf = this;\r
1446         var onAnimComplete = function() {\r
1447             // Finish the collapse\r
1448                 oAnim.onComplete.unsubscribeAll();\r
1450             if(bShow) {\r
1451                 oSelf.containerExpandEvent.fire(oSelf);\r
1452             }\r
1453             else {\r
1454                 oContainer._oContent.style.display = "none";\r
1455                 oSelf.containerCollapseEvent.fire(oSelf);\r
1456             }\r
1457             oSelf._toggleContainerHelpers(bShow);\r
1458         };\r
1460         // Display container and animate it\r
1461         oContainer._oContent.style.display = "block";\r
1462         oAnim.onComplete.subscribe(onAnimComplete);\r
1463         oAnim.animate();\r
1464         this._bContainerOpen = bShow;\r
1465     }\r
1466     // Else don't animate, just show or hide\r
1467     else {\r
1468         if(bShow) {\r
1469             oContainer._oContent.style.display = "block";\r
1470             this.containerExpandEvent.fire(this);\r
1471         }\r
1472         else {\r
1473             oContainer._oContent.style.display = "none";\r
1474             this.containerCollapseEvent.fire(this);\r
1475         }\r
1476         this._toggleContainerHelpers(bShow);\r
1477         this._bContainerOpen = bShow;\r
1478    }\r
1480 };\r
1482 /**\r
1483  * Toggles the highlight on or off for an item in the container, and also cleans\r
1484  * up highlighting of any previous item.\r
1485  *\r
1486  * @method _toggleHighlight\r
1487  * @param oNewItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.\r
1488  * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.\r
1489  * @private\r
1490  */\r
1491 YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(oNewItem, sType) {\r
1492     var sHighlight = this.highlightClassName;\r
1493     if(this._oCurItem) {\r
1494         // Remove highlight from old item\r
1495         YAHOO.util.Dom.removeClass(this._oCurItem, sHighlight);\r
1496     }\r
1498     if((sType == "to") && sHighlight) {\r
1499         // Apply highlight to new item\r
1500         YAHOO.util.Dom.addClass(oNewItem, sHighlight);\r
1501         this._oCurItem = oNewItem;\r
1502     }\r
1503 };\r
1505 /**\r
1506  * Toggles the pre-highlight on or off for an item in the container.\r
1507  *\r
1508  * @method _togglePrehighlight\r
1509  * @param oNewItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.\r
1510  * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.\r
1511  * @private\r
1512  */\r
1513 YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(oNewItem, sType) {\r
1514     if(oNewItem == this._oCurItem) {\r
1515         return;\r
1516     }\r
1518     var sPrehighlight = this.prehighlightClassName;\r
1519     if((sType == "mouseover") && sPrehighlight) {\r
1520         // Apply prehighlight to new item\r
1521         YAHOO.util.Dom.addClass(oNewItem, sPrehighlight);\r
1522     }\r
1523     else {\r
1524         // Remove prehighlight from old item\r
1525         YAHOO.util.Dom.removeClass(oNewItem, sPrehighlight);\r
1526     }\r
1527 };\r
1529 /**\r
1530  * Updates the text input box value with selected query result. If a delimiter\r
1531  * has been defined, then the value gets appended with the delimiter.\r
1532  *\r
1533  * @method _updateValue\r
1534  * @param oItem {HTMLElement} The &lt;li&gt; element item with which to update the value.\r
1535  * @private\r
1536  */\r
1537 YAHOO.widget.AutoComplete.prototype._updateValue = function(oItem) {\r
1538     var oTextbox = this._oTextbox;\r
1539     var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null;\r
1540     var sSavedQuery = this._sSavedQuery;\r
1541     var sResultKey = oItem._sResultKey;\r
1542     oTextbox.focus();\r
1544     // First clear text field\r
1545     oTextbox.value = "";\r
1546     // Grab data to put into text field\r
1547     if(sDelimChar) {\r
1548         if(sSavedQuery) {\r
1549             oTextbox.value = sSavedQuery;\r
1550         }\r
1551         oTextbox.value += sResultKey + sDelimChar;\r
1552         if(sDelimChar != " ") {\r
1553             oTextbox.value += " ";\r
1554         }\r
1555     }\r
1556     else { oTextbox.value = sResultKey; }\r
1558     // scroll to bottom of textarea if necessary\r
1559     if(oTextbox.type == "textarea") {\r
1560         oTextbox.scrollTop = oTextbox.scrollHeight;\r
1561     }\r
1563     // move cursor to end\r
1564     var end = oTextbox.value.length;\r
1565     this._selectText(oTextbox,end,end);\r
1567     this._oCurItem = oItem;\r
1568 };\r
1570 /**\r
1571  * Selects a result item from the container\r
1572  *\r
1573  * @method _selectItem\r
1574  * @param oItem {HTMLElement} The selected &lt;li&gt; element item.\r
1575  * @private\r
1576  */\r
1577 YAHOO.widget.AutoComplete.prototype._selectItem = function(oItem) {\r
1578     this._bItemSelected = true;\r
1579     this._updateValue(oItem);\r
1580     this._cancelIntervalDetection(this);\r
1581     this.itemSelectEvent.fire(this, oItem, oItem._oResultData);\r
1582     this._toggleContainer(false);\r
1583 };\r
1585 /**\r
1586  * For values updated by type-ahead, the right arrow key jumps to the end\r
1587  * of the textbox, otherwise the container is closed.\r
1588  *\r
1589  * @method _jumpSelection\r
1590  * @private\r
1591  */\r
1592 YAHOO.widget.AutoComplete.prototype._jumpSelection = function() {\r
1593     if(!this.typeAhead) {\r
1594         return;\r
1595     }\r
1596     else {\r
1597         this._toggleContainer(false);\r
1598     }\r
1599 };\r
1601 /**\r
1602  * Triggered by up and down arrow keys, changes the current highlighted\r
1603  * &lt;li&gt; element item. Scrolls container if necessary.\r
1604  *\r
1605  * @method _moveSelection\r
1606  * @param nKeyCode {Number} Code of key pressed.\r
1607  * @private\r
1608  */\r
1609 YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) {\r
1610     if(this._bContainerOpen) {\r
1611         // Determine current item's id number\r
1612         var oCurItem = this._oCurItem;\r
1613         var nCurItemIndex = -1;\r
1615         if (oCurItem) {\r
1616             nCurItemIndex = oCurItem._nItemIndex;\r
1617         }\r
1619         var nNewItemIndex = (nKeyCode == 40) ?\r
1620                 (nCurItemIndex + 1) : (nCurItemIndex - 1);\r
1622         // Out of bounds\r
1623         if (nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) {\r
1624             return;\r
1625         }\r
1627         if (oCurItem) {\r
1628             // Unhighlight current item\r
1629             this._toggleHighlight(oCurItem, "from");\r
1630             this.itemArrowFromEvent.fire(this, oCurItem);\r
1631         }\r
1632         if (nNewItemIndex == -1) {\r
1633            // Go back to query (remove type-ahead string)\r
1634             if(this.delimChar && this._sSavedQuery) {\r
1635                 if (!this._textMatchesOption()) {\r
1636                     this._oTextbox.value = this._sSavedQuery;\r
1637                 }\r
1638                 else {\r
1639                     this._oTextbox.value = this._sSavedQuery + this._sCurQuery;\r
1640                 }\r
1641             }\r
1642             else {\r
1643                 this._oTextbox.value = this._sCurQuery;\r
1644             }\r
1645             this._oCurItem = null;\r
1646             return;\r
1647         }\r
1648         if (nNewItemIndex == -2) {\r
1649             // Close container\r
1650             this._toggleContainer(false);\r
1651             return;\r
1652         }\r
1654         var oNewItem = this._aListItems[nNewItemIndex];\r
1656         // Scroll the container if necessary\r
1657         var oContent = this._oContainer._oContent;\r
1658         var scrollOn = ((YAHOO.util.Dom.getStyle(oContent,"overflow") == "auto") ||\r
1659             (YAHOO.util.Dom.getStyle(oContent,"overflowY") == "auto"));\r
1660         if(scrollOn && (nNewItemIndex > -1) &&\r
1661         (nNewItemIndex < this._nDisplayedItems)) {\r
1662             // User is keying down\r
1663             if(nKeyCode == 40) {\r
1664                 // Bottom of selected item is below scroll area...\r
1665                 if((oNewItem.offsetTop+oNewItem.offsetHeight) > (oContent.scrollTop + oContent.offsetHeight)) {\r
1666                     // Set bottom of scroll area to bottom of selected item\r
1667                     oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight;\r
1668                 }\r
1669                 // Bottom of selected item is above scroll area...\r
1670                 else if((oNewItem.offsetTop+oNewItem.offsetHeight) < oContent.scrollTop) {\r
1671                     // Set top of selected item to top of scroll area\r
1672                     oContent.scrollTop = oNewItem.offsetTop;\r
1674                 }\r
1675             }\r
1676             // User is keying up\r
1677             else {\r
1678                 // Top of selected item is above scroll area\r
1679                 if(oNewItem.offsetTop < oContent.scrollTop) {\r
1680                     // Set top of scroll area to top of selected item\r
1681                     this._oContainer._oContent.scrollTop = oNewItem.offsetTop;\r
1682                 }\r
1683                 // Top of selected item is below scroll area\r
1684                 else if(oNewItem.offsetTop > (oContent.scrollTop + oContent.offsetHeight)) {\r
1685                     // Set bottom of selected item to bottom of scroll area\r
1686                     this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight;\r
1687                 }\r
1688             }\r
1689         }\r
1691         this._toggleHighlight(oNewItem, "to");\r
1692         this.itemArrowToEvent.fire(this, oNewItem);\r
1693         if(this.typeAhead) {\r
1694             this._updateValue(oNewItem);\r
1695         }\r
1696     }\r
1697 };\r
1699 /////////////////////////////////////////////////////////////////////////////\r
1700 //\r
1701 // Private event handlers\r
1702 //\r
1703 /////////////////////////////////////////////////////////////////////////////\r
1705 /**\r
1706  * Handles &lt;li&gt; element mouseover events in the container.\r
1707  *\r
1708  * @method _onItemMouseover\r
1709  * @param v {HTMLEvent} The mouseover event.\r
1710  * @param oSelf {Object} The AutoComplete instance.\r
1711  * @private\r
1712  */\r
1713 YAHOO.widget.AutoComplete.prototype._onItemMouseover = function(v,oSelf) {\r
1714     if(oSelf.prehighlightClassName) {\r
1715         oSelf._togglePrehighlight(this,"mouseover");\r
1716     }\r
1717     else {\r
1718         oSelf._toggleHighlight(this,"to");\r
1719     }\r
1721     oSelf.itemMouseOverEvent.fire(oSelf, this);\r
1722 };\r
1724 /**\r
1725  * Handles &lt;li&gt; element mouseout events in the container.\r
1726  *\r
1727  * @method _onItemMouseout\r
1728  * @param v {HTMLEvent} The mouseout event.\r
1729  * @param oSelf {Object} The AutoComplete instance.\r
1730  * @private\r
1731  */\r
1732 YAHOO.widget.AutoComplete.prototype._onItemMouseout = function(v,oSelf) {\r
1733     if(oSelf.prehighlightClassName) {\r
1734         oSelf._togglePrehighlight(this,"mouseout");\r
1735     }\r
1736     else {\r
1737         oSelf._toggleHighlight(this,"from");\r
1738     }\r
1740     oSelf.itemMouseOutEvent.fire(oSelf, this);\r
1741 };\r
1743 /**\r
1744  * Handles &lt;li&gt; element click events in the container.\r
1745  *\r
1746  * @method _onItemMouseclick\r
1747  * @param v {HTMLEvent} The click event.\r
1748  * @param oSelf {Object} The AutoComplete instance.\r
1749  * @private\r
1750  */\r
1751 YAHOO.widget.AutoComplete.prototype._onItemMouseclick = function(v,oSelf) {\r
1752     // In case item has not been moused over\r
1753     oSelf._toggleHighlight(this,"to");\r
1754     oSelf._selectItem(this);\r
1755 };\r
1757 /**\r
1758  * Handles container mouseover events.\r
1759  *\r
1760  * @method _onContainerMouseover\r
1761  * @param v {HTMLEvent} The mouseover event.\r
1762  * @param oSelf {Object} The AutoComplete instance.\r
1763  * @private\r
1764  */\r
1765 YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) {\r
1766     oSelf._bOverContainer = true;\r
1767 };\r
1769 /**\r
1770  * Handles container mouseout events.\r
1771  *\r
1772  * @method _onContainerMouseout\r
1773  * @param v {HTMLEvent} The mouseout event.\r
1774  * @param oSelf {Object} The AutoComplete instance.\r
1775  * @private\r
1776  */\r
1777 YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) {\r
1778     oSelf._bOverContainer = false;\r
1779     // If container is still active\r
1780     if(oSelf._oCurItem) {\r
1781         oSelf._toggleHighlight(oSelf._oCurItem,"to");\r
1782     }\r
1783 };\r
1785 /**\r
1786  * Handles container scroll events.\r
1787  *\r
1788  * @method _onContainerScroll\r
1789  * @param v {HTMLEvent} The scroll event.\r
1790  * @param oSelf {Object} The AutoComplete instance.\r
1791  * @private\r
1792  */\r
1793 YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) {\r
1794     oSelf._oTextbox.focus();\r
1795 };\r
1797 /**\r
1798  * Handles container resize events.\r
1799  *\r
1800  * @method _onContainerResize\r
1801  * @param v {HTMLEvent} The resize event.\r
1802  * @param oSelf {Object} The AutoComplete instance.\r
1803  * @private\r
1804  */\r
1805 YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) {\r
1806     oSelf._toggleContainerHelpers(oSelf._bContainerOpen);\r
1807 };\r
1810 /**\r
1811  * Handles textbox keydown events of functional keys, mainly for UI behavior.\r
1812  *\r
1813  * @method _onTextboxKeyDown\r
1814  * @param v {HTMLEvent} The keydown event.\r
1815  * @param oSelf {object} The AutoComplete instance.\r
1816  * @private\r
1817  */\r
1818 YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) {\r
1819     var nKeyCode = v.keyCode;\r
1821     switch (nKeyCode) {\r
1822         case 9: // tab\r
1823             if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {\r
1824                 if(oSelf._bContainerOpen) {\r
1825                     YAHOO.util.Event.stopEvent(v);\r
1826                 }\r
1827             }\r
1828             // select an item or clear out\r
1829             if(oSelf._oCurItem) {\r
1830                 oSelf._selectItem(oSelf._oCurItem);\r
1831             }\r
1832             else {\r
1833                 oSelf._toggleContainer(false);\r
1834             }\r
1835             break;\r
1836         case 13: // enter\r
1837             if(oSelf._nKeyCode != nKeyCode) {\r
1838                 if(oSelf._bContainerOpen) {\r
1839                     YAHOO.util.Event.stopEvent(v);\r
1840                 }\r
1841             }\r
1842             if(oSelf._oCurItem) {\r
1843                 oSelf._selectItem(oSelf._oCurItem);\r
1844             }\r
1845             else {\r
1846                 oSelf._toggleContainer(false);\r
1847             }\r
1848             break;\r
1849         case 27: // esc\r
1850             oSelf._toggleContainer(false);\r
1851             return;\r
1852         case 39: // right\r
1853             oSelf._jumpSelection();\r
1854             break;\r
1855         case 38: // up\r
1856             YAHOO.util.Event.stopEvent(v);\r
1857             oSelf._moveSelection(nKeyCode);\r
1858             break;\r
1859         case 40: // down\r
1860             YAHOO.util.Event.stopEvent(v);\r
1861             oSelf._moveSelection(nKeyCode);\r
1862             break;\r
1863         default:\r
1864             break;\r
1865     }\r
1866 };\r
1868 /**\r
1869  * Handles textbox keypress events.\r
1870  * @method _onTextboxKeyPress\r
1871  * @param v {HTMLEvent} The keypress event.\r
1872  * @param oSelf {Object} The AutoComplete instance.\r
1873  * @private\r
1874  */\r
1875 YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) {\r
1876     var nKeyCode = v.keyCode;\r
1878         //Expose only to Mac browsers, where stopEvent is ineffective on keydown events (bug 790337)\r
1879         var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1);\r
1880         if(isMac) {\r
1881             switch (nKeyCode) {\r
1882             case 9: // tab\r
1883                 if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {\r
1884                     if(oSelf._bContainerOpen) {\r
1885                         YAHOO.util.Event.stopEvent(v);\r
1886                     }\r
1887                 }\r
1888                 break;\r
1889             case 13: // enter\r
1890                     if(oSelf._nKeyCode != nKeyCode) {\r
1891                         if(oSelf._bContainerOpen) {\r
1892                             YAHOO.util.Event.stopEvent(v);\r
1893                         }\r
1894                     }\r
1895                 break;\r
1896             case 38: // up\r
1897             case 40: // down\r
1898                 YAHOO.util.Event.stopEvent(v);\r
1899                 break;\r
1900             default:\r
1901                 break;\r
1902             }\r
1903         }\r
1905         //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948)\r
1906         // Korean IME detected\r
1907         else if(nKeyCode == 229) {\r
1908             oSelf._queryInterval = setInterval(function() { oSelf._onIMEDetected(oSelf); },500);\r
1909         }\r
1910 };\r
1912 /**\r
1913  * Handles textbox keyup events that trigger queries.\r
1914  *\r
1915  * @method _onTextboxKeyUp\r
1916  * @param v {HTMLEvent} The keyup event.\r
1917  * @param oSelf {Object} The AutoComplete instance.\r
1918  * @private\r
1919  */\r
1920 YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) {\r
1921     // Check to see if any of the public properties have been updated\r
1922     oSelf._initProps();\r
1924     var nKeyCode = v.keyCode;\r
1925     oSelf._nKeyCode = nKeyCode;\r
1926     var sText = this.value; //string in textbox\r
1928     // Filter out chars that don't trigger queries\r
1929     if (oSelf._isIgnoreKey(nKeyCode) || (sText.toLowerCase() == oSelf._sCurQuery)) {\r
1930         return;\r
1931     }\r
1932     else {\r
1933         oSelf.textboxKeyEvent.fire(oSelf, nKeyCode);\r
1934     }\r
1936     // Set timeout on the request\r
1937     if (oSelf.queryDelay > 0) {\r
1938         var nDelayID =\r
1939             setTimeout(function(){oSelf._sendQuery(sText);},(oSelf.queryDelay * 1000));\r
1941         if (oSelf._nDelayID != -1) {\r
1942             clearTimeout(oSelf._nDelayID);\r
1943         }\r
1945         oSelf._nDelayID = nDelayID;\r
1946     }\r
1947     else {\r
1948         // No delay so send request immediately\r
1949         oSelf._sendQuery(sText);\r
1950     }\r
1951 };\r
1953 /**\r
1954  * Handles text input box receiving focus.\r
1955  *\r
1956  * @method _onTextboxFocus\r
1957  * @param v {HTMLEvent} The focus event.\r
1958  * @param oSelf {Object} The AutoComplete instance.\r
1959  * @private\r
1960  */\r
1961 YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) {\r
1962     oSelf._oTextbox.setAttribute("autocomplete","off");\r
1963     oSelf._bFocused = true;\r
1964     oSelf.textboxFocusEvent.fire(oSelf);\r
1965 };\r
1967 /**\r
1968  * Handles text input box losing focus.\r
1969  *\r
1970  * @method _onTextboxBlur\r
1971  * @param v {HTMLEvent} The focus event.\r
1972  * @param oSelf {Object} The AutoComplete instance.\r
1973  * @private\r
1974  */\r
1975 YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) {\r
1976     // Don't treat as a blur if it was a selection via mouse click\r
1977     if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) {\r
1978         // Current query needs to be validated\r
1979         if(!oSelf._bItemSelected) {\r
1980             if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && !oSelf._textMatchesOption())) {\r
1981                 if(oSelf.forceSelection) {\r
1982                     oSelf._clearSelection();\r
1983                 }\r
1984                 else {\r
1985                     oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery);\r
1986                 }\r
1987             }\r
1988         }\r
1990         if(oSelf._bContainerOpen) {\r
1991             oSelf._toggleContainer(false);\r
1992         }\r
1993         oSelf._cancelIntervalDetection(oSelf);\r
1994         oSelf._bFocused = false;\r
1995         oSelf.textboxBlurEvent.fire(oSelf);\r
1996     }\r
1997 };\r
1999 /**\r
2000  * Handles form submission event.\r
2001  *\r
2002  * @method _onFormSubmit\r
2003  * @param v {HTMLEvent} The submit event.\r
2004  * @param oSelf {Object} The AutoComplete instance.\r
2005  * @private\r
2006  */\r
2007 YAHOO.widget.AutoComplete.prototype._onFormSubmit = function(v,oSelf) {\r
2008     if(oSelf.allowBrowserAutocomplete) {\r
2009         oSelf._oTextbox.setAttribute("autocomplete","on");\r
2010     }\r
2011     else {\r
2012         oSelf._oTextbox.setAttribute("autocomplete","off");\r
2013     }\r
2014 };\r
2015 /****************************************************************************/\r
2016 /****************************************************************************/\r
2017 /****************************************************************************/\r
2019 /**\r
2020  * The DataSource classes manages sending a request and returning response from a live\r
2021  * database. Supported data include local JavaScript arrays and objects and databases\r
2022  * accessible via XHR connections. Supported response formats include JavaScript arrays,\r
2023  * JSON, XML, and flat-file textual data.\r
2024  *  \r
2025  * @class DataSource\r
2026  * @constructor\r
2027  */\r
2028 YAHOO.widget.DataSource = function() { \r
2029     /* abstract class */\r
2030 };\r
2033 /////////////////////////////////////////////////////////////////////////////\r
2034 //\r
2035 // Public constants\r
2036 //\r
2037 /////////////////////////////////////////////////////////////////////////////\r
2039 /**\r
2040  * Error message for null data responses.\r
2041  *\r
2042  * @property ERROR_DATANULL\r
2043  * @type String\r
2044  * @static\r
2045  * @final\r
2046  */\r
2047 YAHOO.widget.DataSource.ERROR_DATANULL = "Response data was null";\r
2049 /**\r
2050  * Error message for data responses with parsing errors.\r
2051  *\r
2052  * @property ERROR_DATAPARSE\r
2053  * @type String\r
2054  * @static\r
2055  * @final\r
2056  */\r
2057 YAHOO.widget.DataSource.ERROR_DATAPARSE = "Response data could not be parsed";\r
2060 /////////////////////////////////////////////////////////////////////////////\r
2061 //\r
2062 // Public member variables\r
2063 //\r
2064 /////////////////////////////////////////////////////////////////////////////\r
2066 /**\r
2067  * Max size of the local cache.  Set to 0 to turn off caching.  Caching is\r
2068  * useful to reduce the number of server connections.  Recommended only for data\r
2069  * sources that return comprehensive results for queries or when stale data is\r
2070  * not an issue.\r
2071  *\r
2072  * @property maxCacheEntries\r
2073  * @type Number\r
2074  * @default 15\r
2075  */\r
2076 YAHOO.widget.DataSource.prototype.maxCacheEntries = 15;\r
2078 /**\r
2079  * Use this to equate cache matching with the type of matching done by your live\r
2080  * data source. If caching is on and queryMatchContains is true, the cache\r
2081  * returns results that "contain" the query string. By default,\r
2082  * queryMatchContains is set to false, meaning the cache only returns results\r
2083  * that "start with" the query string.\r
2084  *\r
2085  * @property queryMatchContains\r
2086  * @type Boolean\r
2087  * @default false\r
2088  */\r
2089 YAHOO.widget.DataSource.prototype.queryMatchContains = false;\r
2091 /**\r
2092  * Enables query subset matching. If caching is on and queryMatchSubset is\r
2093  * true, substrings of queries will return matching cached results. For\r
2094  * instance, if the first query is for "abc" susequent queries that start with\r
2095  * "abc", like "abcd", will be queried against the cache, and not the live data\r
2096  * source. Recommended only for DataSources that return comprehensive results\r
2097  * for queries with very few characters.\r
2098  *\r
2099  * @property queryMatchSubset\r
2100  * @type Boolean\r
2101  * @default false\r
2102  *\r
2103  */\r
2104 YAHOO.widget.DataSource.prototype.queryMatchSubset = false;\r
2106 /**\r
2107  * Enables query case-sensitivity matching. If caching is on and\r
2108  * queryMatchCase is true, queries will only return results for case-sensitive\r
2109  * matches.\r
2110  *\r
2111  * @property queryMatchCase\r
2112  * @type Boolean\r
2113  * @default false\r
2114  */\r
2115 YAHOO.widget.DataSource.prototype.queryMatchCase = false;\r
2118 /////////////////////////////////////////////////////////////////////////////\r
2119 //\r
2120 // Public methods\r
2121 //\r
2122 /////////////////////////////////////////////////////////////////////////////\r
2124  /**\r
2125  * Public accessor to the unique name of the DataSource instance.\r
2126  *\r
2127  * @method toString\r
2128  * @return {String} Unique name of the DataSource instance\r
2129  */\r
2130 YAHOO.widget.DataSource.prototype.toString = function() {\r
2131     return "DataSource " + this._sName;\r
2132 };\r
2134 /**\r
2135  * Retrieves query results, first checking the local cache, then making the\r
2136  * query request to the live data source as defined by the function doQuery.\r
2137  *\r
2138  * @method getResults\r
2139  * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.\r
2140  * @param sQuery {String} Query string.\r
2141  * @param oParent {Object} The object instance that has requested data.\r
2142  */\r
2143 YAHOO.widget.DataSource.prototype.getResults = function(oCallbackFn, sQuery, oParent) {\r
2144     \r
2145     // First look in cache\r
2146     var aResults = this._doQueryCache(oCallbackFn,sQuery,oParent);\r
2147     \r
2148     // Not in cache, so get results from server\r
2149     if(aResults.length === 0) {\r
2150         this.queryEvent.fire(this, oParent, sQuery);\r
2151         this.doQuery(oCallbackFn, sQuery, oParent);\r
2152     }\r
2153 };\r
2155 /**\r
2156  * Abstract method implemented by subclasses to make a query to the live data\r
2157  * source. Must call the callback function with the response returned from the\r
2158  * query. Populates cache (if enabled).\r
2159  *\r
2160  * @method doQuery\r
2161  * @param oCallbackFn {HTMLFunction} Callback function implemented by oParent to which to return results.\r
2162  * @param sQuery {String} Query string.\r
2163  * @param oParent {Object} The object instance that has requested data.\r
2164  */\r
2165 YAHOO.widget.DataSource.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {\r
2166     /* override this */ \r
2167 };\r
2169 /**\r
2170  * Flushes cache.\r
2171  *\r
2172  * @method flushCache\r
2173  */\r
2174 YAHOO.widget.DataSource.prototype.flushCache = function() {\r
2175     if(this._aCache) {\r
2176         this._aCache = [];\r
2177     }\r
2178     if(this._aCacheHelper) {\r
2179         this._aCacheHelper = [];\r
2180     }\r
2181     this.cacheFlushEvent.fire(this);\r
2182 };\r
2184 /////////////////////////////////////////////////////////////////////////////\r
2185 //\r
2186 // Public events\r
2187 //\r
2188 /////////////////////////////////////////////////////////////////////////////\r
2190 /**\r
2191  * Fired when a query is made to the live data source.\r
2192  *\r
2193  * @event queryEvent\r
2194  * @param oSelf {Object} The DataSource instance.\r
2195  * @param oParent {Object} The requesting object.\r
2196  * @param sQuery {String} The query string.\r
2197  */\r
2198 YAHOO.widget.DataSource.prototype.queryEvent = null;\r
2200 /**\r
2201  * Fired when a query is made to the local cache.\r
2202  *\r
2203  * @event cacheQueryEvent\r
2204  * @param oSelf {Object} The DataSource instance.\r
2205  * @param oParent {Object} The requesting object.\r
2206  * @param sQuery {String} The query string.\r
2207  */\r
2208 YAHOO.widget.DataSource.prototype.cacheQueryEvent = null;\r
2210 /**\r
2211  * Fired when data is retrieved from the live data source.\r
2212  *\r
2213  * @event getResultsEvent\r
2214  * @param oSelf {Object} The DataSource instance.\r
2215  * @param oParent {Object} The requesting object.\r
2216  * @param sQuery {String} The query string.\r
2217  * @param aResults {Object[]} Array of result objects.\r
2218  */\r
2219 YAHOO.widget.DataSource.prototype.getResultsEvent = null;\r
2220     \r
2221 /**\r
2222  * Fired when data is retrieved from the local cache.\r
2223  *\r
2224  * @event getCachedResultsEvent\r
2225  * @param oSelf {Object} The DataSource instance.\r
2226  * @param oParent {Object} The requesting object.\r
2227  * @param sQuery {String} The query string.\r
2228  * @param aResults {Object[]} Array of result objects.\r
2229  */\r
2230 YAHOO.widget.DataSource.prototype.getCachedResultsEvent = null;\r
2232 /**\r
2233  * Fired when an error is encountered with the live data source.\r
2234  *\r
2235  * @event dataErrorEvent\r
2236  * @param oSelf {Object} The DataSource instance.\r
2237  * @param oParent {Object} The requesting object.\r
2238  * @param sQuery {String} The query string.\r
2239  * @param sMsg {String} Error message string\r
2240  */\r
2241 YAHOO.widget.DataSource.prototype.dataErrorEvent = null;\r
2243 /**\r
2244  * Fired when the local cache is flushed.\r
2245  *\r
2246  * @event cacheFlushEvent\r
2247  * @param oSelf {Object} The DataSource instance\r
2248  */\r
2249 YAHOO.widget.DataSource.prototype.cacheFlushEvent = null;\r
2251 /////////////////////////////////////////////////////////////////////////////\r
2252 //\r
2253 // Private member variables\r
2254 //\r
2255 /////////////////////////////////////////////////////////////////////////////\r
2257 /**\r
2258  * Internal class variable to index multiple DataSource instances.\r
2259  *\r
2260  * @property _nIndex\r
2261  * @type Number\r
2262  * @private\r
2263  * @static\r
2264  */\r
2265 YAHOO.widget.DataSource._nIndex = 0;\r
2267 /**\r
2268  * Name of DataSource instance.\r
2269  *\r
2270  * @property _sName\r
2271  * @type String\r
2272  * @private\r
2273  */\r
2274 YAHOO.widget.DataSource.prototype._sName = null;\r
2276 /**\r
2277  * Local cache of data result objects indexed chronologically.\r
2278  *\r
2279  * @property _aCache\r
2280  * @type Object[]\r
2281  * @private\r
2282  */\r
2283 YAHOO.widget.DataSource.prototype._aCache = null;\r
2286 /////////////////////////////////////////////////////////////////////////////\r
2287 //\r
2288 // Private methods\r
2289 //\r
2290 /////////////////////////////////////////////////////////////////////////////\r
2292 /**\r
2293  * Initializes DataSource instance.\r
2294  *  \r
2295  * @method _init\r
2296  * @private\r
2297  */\r
2298 YAHOO.widget.DataSource.prototype._init = function() {\r
2299     // Validate and initialize public configs\r
2300     var maxCacheEntries = this.maxCacheEntries;\r
2301     if(isNaN(maxCacheEntries) || (maxCacheEntries < 0)) {\r
2302         maxCacheEntries = 0;\r
2303     }\r
2304     // Initialize local cache\r
2305     if(maxCacheEntries > 0 && !this._aCache) {\r
2306         this._aCache = [];\r
2307     }\r
2308     \r
2309     this._sName = "instance" + YAHOO.widget.DataSource._nIndex;\r
2310     YAHOO.widget.DataSource._nIndex++;\r
2311     \r
2312     this.queryEvent = new YAHOO.util.CustomEvent("query", this);\r
2313     this.cacheQueryEvent = new YAHOO.util.CustomEvent("cacheQuery", this);\r
2314     this.getResultsEvent = new YAHOO.util.CustomEvent("getResults", this);\r
2315     this.getCachedResultsEvent = new YAHOO.util.CustomEvent("getCachedResults", this);\r
2316     this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);\r
2317     this.cacheFlushEvent = new YAHOO.util.CustomEvent("cacheFlush", this);\r
2318 };\r
2320 /**\r
2321  * Adds a result object to the local cache, evicting the oldest element if the \r
2322  * cache is full. Newer items will have higher indexes, the oldest item will have\r
2323  * index of 0. \r
2324  *\r
2325  * @method _addCacheElem\r
2326  * @param oResult {Object} Data result object, including array of results.\r
2327  * @private\r
2328  */\r
2329 YAHOO.widget.DataSource.prototype._addCacheElem = function(oResult) {\r
2330     var aCache = this._aCache;\r
2331     // Don't add if anything important is missing.\r
2332     if(!aCache || !oResult || !oResult.query || !oResult.results) {\r
2333         return;\r
2334     }\r
2335     \r
2336     // If the cache is full, make room by removing from index=0\r
2337     if(aCache.length >= this.maxCacheEntries) {\r
2338         aCache.shift();\r
2339     }\r
2340         \r
2341     // Add to cache, at the end of the array\r
2342     aCache.push(oResult);\r
2343 };\r
2345 /**\r
2346  * Queries the local cache for results. If query has been cached, the callback\r
2347  * function is called with the results, and the cached is refreshed so that it\r
2348  * is now the newest element.  \r
2349  *\r
2350  * @method _doQueryCache\r
2351  * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.\r
2352  * @param sQuery {String} Query string.\r
2353  * @param oParent {Object} The object instance that has requested data.\r
2354  * @return aResults {Object[]} Array of results from local cache if found, otherwise null.\r
2355  * @private \r
2356  */\r
2357 YAHOO.widget.DataSource.prototype._doQueryCache = function(oCallbackFn, sQuery, oParent) {\r
2358     var aResults = [];\r
2359     var bMatchFound = false;\r
2360     var aCache = this._aCache;\r
2361     var nCacheLength = (aCache) ? aCache.length : 0;\r
2362     var bMatchContains = this.queryMatchContains;\r
2363     \r
2364     // If cache is enabled...\r
2365     if((this.maxCacheEntries > 0) && aCache && (nCacheLength > 0)) {\r
2366         this.cacheQueryEvent.fire(this, oParent, sQuery);\r
2367         // If case is unimportant, normalize query now instead of in loops\r
2368         if(!this.queryMatchCase) {\r
2369             var sOrigQuery = sQuery;\r
2370             sQuery = sQuery.toLowerCase();\r
2371         }\r
2373         // Loop through each cached element's query property...\r
2374         for(var i = nCacheLength-1; i >= 0; i--) {\r
2375             var resultObj = aCache[i];\r
2376             var aAllResultItems = resultObj.results;\r
2377             // If case is unimportant, normalize match key for comparison\r
2378             var matchKey = (!this.queryMatchCase) ?\r
2379                 encodeURIComponent(resultObj.query).toLowerCase():\r
2380                 encodeURIComponent(resultObj.query);\r
2381             \r
2382             // If a cached match key exactly matches the query...\r
2383             if(matchKey == sQuery) {\r
2384                     // Stash all result objects into aResult[] and stop looping through the cache.\r
2385                     bMatchFound = true;\r
2386                     aResults = aAllResultItems;\r
2387                     \r
2388                     // The matching cache element was not the most recent,\r
2389                     // so now we need to refresh the cache.\r
2390                     if(i != nCacheLength-1) {                        \r
2391                         // Remove element from its original location\r
2392                         aCache.splice(i,1);\r
2393                         // Add element as newest\r
2394                         this._addCacheElem(resultObj);\r
2395                     }\r
2396                     break;\r
2397             }\r
2398             // Else if this query is not an exact match and subset matching is enabled...\r
2399             else if(this.queryMatchSubset) {\r
2400                 // Loop through substrings of each cached element's query property...\r
2401                 for(var j = sQuery.length-1; j >= 0 ; j--) {\r
2402                     var subQuery = sQuery.substr(0,j);\r
2403                     \r
2404                     // If a substring of a cached sQuery exactly matches the query...\r
2405                     if(matchKey == subQuery) {                    \r
2406                         bMatchFound = true;\r
2407                         \r
2408                         // Go through each cached result object to match against the query...\r
2409                         for(var k = aAllResultItems.length-1; k >= 0; k--) {\r
2410                             var aRecord = aAllResultItems[k];\r
2411                             var sKeyIndex = (this.queryMatchCase) ?\r
2412                                 encodeURIComponent(aRecord[0]).indexOf(sQuery):\r
2413                                 encodeURIComponent(aRecord[0]).toLowerCase().indexOf(sQuery);\r
2414                             \r
2415                             // A STARTSWITH match is when the query is found at the beginning of the key string...\r
2416                             if((!bMatchContains && (sKeyIndex === 0)) ||\r
2417                             // A CONTAINS match is when the query is found anywhere within the key string...\r
2418                             (bMatchContains && (sKeyIndex > -1))) {\r
2419                                 // Stash a match into aResults[].\r
2420                                 aResults.unshift(aRecord);\r
2421                             }\r
2422                         }\r
2423                         \r
2424                         // Add the subset match result set object as the newest element to cache,\r
2425                         // and stop looping through the cache.\r
2426                         resultObj = {};\r
2427                         resultObj.query = sQuery;\r
2428                         resultObj.results = aResults;\r
2429                         this._addCacheElem(resultObj);\r
2430                         break;\r
2431                     }\r
2432                 }\r
2433                 if(bMatchFound) {\r
2434                     break;\r
2435                 }\r
2436             }\r
2437         }\r
2438         \r
2439         // If there was a match, send along the results.\r
2440         if(bMatchFound) {\r
2441             this.getCachedResultsEvent.fire(this, oParent, sOrigQuery, aResults);\r
2442             oCallbackFn(sOrigQuery, aResults, oParent);\r
2443         }\r
2444     }\r
2445     return aResults;\r
2446 };\r
2449 /****************************************************************************/\r
2450 /****************************************************************************/\r
2451 /****************************************************************************/\r
2453 /**\r
2454  * Implementation of YAHOO.widget.DataSource using XML HTTP requests that return\r
2455  * query results.\r
2456  *  \r
2457  * @class DS_XHR\r
2458  * @extends YAHOO.widget.DataSource\r
2459  * @requires connection\r
2460  * @constructor\r
2461  * @param sScriptURI {String} Absolute or relative URI to script that returns query\r
2462  * results as JSON, XML, or delimited flat-file data.\r
2463  * @param aSchema {String[]} Data schema definition of results.\r
2464  * @param oConfigs {Object} (optional) Object literal of config params.\r
2465  */\r
2466 YAHOO.widget.DS_XHR = function(sScriptURI, aSchema, oConfigs) {\r
2467     // Set any config params passed in to override defaults\r
2468     if(typeof oConfigs == "object") {\r
2469         for(var sConfig in oConfigs) {\r
2470             this[sConfig] = oConfigs[sConfig];\r
2471         }\r
2472     }\r
2473     \r
2474     // Initialization sequence\r
2475     if(!aSchema || (aSchema.constructor != Array)) {\r
2476         YAHOO.log("Could not instantiate XHR DataSource due to invalid arguments", "error", this.toString());\r
2477         return;\r
2478     }\r
2479     else {\r
2480         this.schema = aSchema;\r
2481     }\r
2482     this.scriptURI = sScriptURI;\r
2483     this._init();\r
2484     YAHOO.log("XHR DataSource initialized","info",this.toString());\r
2485 };\r
2487 YAHOO.widget.DS_XHR.prototype = new YAHOO.widget.DataSource();\r
2489 /////////////////////////////////////////////////////////////////////////////\r
2490 //\r
2491 // Public constants\r
2492 //\r
2493 /////////////////////////////////////////////////////////////////////////////\r
2495 /**\r
2496  * JSON data type.\r
2497  *\r
2498  * @property TYPE_JSON\r
2499  * @type Number\r
2500  * @static\r
2501  * @final\r
2502  */\r
2503 YAHOO.widget.DS_XHR.TYPE_JSON = 0;\r
2505 /**\r
2506  * XML data type.\r
2507  *\r
2508  * @property TYPE_XML\r
2509  * @type Number\r
2510  * @static\r
2511  * @final\r
2512  */\r
2513 YAHOO.widget.DS_XHR.TYPE_XML = 1;\r
2515 /**\r
2516  * Flat-file data type.\r
2517  *\r
2518  * @property TYPE_FLAT\r
2519  * @type Number\r
2520  * @static\r
2521  * @final\r
2522  */\r
2523 YAHOO.widget.DS_XHR.TYPE_FLAT = 2;\r
2525 /**\r
2526  * Error message for XHR failure.\r
2527  *\r
2528  * @property ERROR_DATAXHR\r
2529  * @type String\r
2530  * @static\r
2531  * @final\r
2532  */\r
2533 YAHOO.widget.DS_XHR.ERROR_DATAXHR = "XHR response failed";\r
2535 /////////////////////////////////////////////////////////////////////////////\r
2536 //\r
2537 // Public member variables\r
2538 //\r
2539 /////////////////////////////////////////////////////////////////////////////\r
2541 /**\r
2542  * Alias to YUI Connection Manager. Allows implementers to specify their own\r
2543  * subclasses of the YUI Connection Manager utility.\r
2544  *\r
2545  * @property connMgr\r
2546  * @type Object\r
2547  * @default YAHOO.util.Connect\r
2548  */\r
2549 YAHOO.widget.DS_XHR.prototype.connMgr = YAHOO.util.Connect;\r
2551 /**\r
2552  * Number of milliseconds the XHR connection will wait for a server response. A\r
2553  * a value of zero indicates the XHR connection will wait forever. Any value\r
2554  * greater than zero will use the Connection utility's Auto-Abort feature.\r
2555  *\r
2556  * @property connTimeout\r
2557  * @type Number\r
2558  * @default 0\r
2559  */\r
2560 YAHOO.widget.DS_XHR.prototype.connTimeout = 0;\r
2562 /**\r
2563  * Absolute or relative URI to script that returns query results. For instance,\r
2564  * queries will be sent to &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput\r
2565  *\r
2566  * @property scriptURI\r
2567  * @type String\r
2568  */\r
2569 YAHOO.widget.DS_XHR.prototype.scriptURI = null;\r
2571 /**\r
2572  * Query string parameter name sent to scriptURI. For instance, queries will be\r
2573  * sent to &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput\r
2574  *\r
2575  * @property scriptQueryParam\r
2576  * @type String\r
2577  * @default "query"\r
2578  */\r
2579 YAHOO.widget.DS_XHR.prototype.scriptQueryParam = "query";\r
2581 /**\r
2582  * String of key/value pairs to append to requests made to scriptURI. Define\r
2583  * this string when you want to send additional query parameters to your script.\r
2584  * When defined, queries will be sent to\r
2585  * &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput&#38;&#60;scriptQueryAppend&#62;\r
2586  *\r
2587  * @property scriptQueryAppend\r
2588  * @type String\r
2589  * @default ""\r
2590  */\r
2591 YAHOO.widget.DS_XHR.prototype.scriptQueryAppend = "";\r
2593 /**\r
2594  * XHR response data type. Other types that may be defined are YAHOO.widget.DS_XHR.TYPE_XML\r
2595  * and YAHOO.widget.DS_XHR.TYPE_FLAT.\r
2596  *\r
2597  * @property responseType\r
2598  * @type String\r
2599  * @default YAHOO.widget.DS_XHR.TYPE_JSON\r
2600  */\r
2601 YAHOO.widget.DS_XHR.prototype.responseType = YAHOO.widget.DS_XHR.TYPE_JSON;\r
2603 /**\r
2604  * String after which to strip results. If the results from the XHR are sent\r
2605  * back as HTML, the gzip HTML comment appears at the end of the data and should\r
2606  * be ignored.\r
2607  *\r
2608  * @property responseStripAfter\r
2609  * @type String\r
2610  * @default "\n&#60;!-"\r
2611  */\r
2612 YAHOO.widget.DS_XHR.prototype.responseStripAfter = "\n<!-";\r
2614 /////////////////////////////////////////////////////////////////////////////\r
2615 //\r
2616 // Public methods\r
2617 //\r
2618 /////////////////////////////////////////////////////////////////////////////\r
2620 /**\r
2621  * Queries the live data source defined by scriptURI for results. Results are\r
2622  * passed back to a callback function.\r
2623  *  \r
2624  * @method doQuery\r
2625  * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.\r
2626  * @param sQuery {String} Query string.\r
2627  * @param oParent {Object} The object instance that has requested data.\r
2628  */\r
2629 YAHOO.widget.DS_XHR.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {\r
2630     var isXML = (this.responseType == YAHOO.widget.DS_XHR.TYPE_XML);\r
2631     var sUri = this.scriptURI+"?"+this.scriptQueryParam+"="+sQuery;\r
2632     if(this.scriptQueryAppend.length > 0) {\r
2633         sUri += "&" + this.scriptQueryAppend;\r
2634     }\r
2635     YAHOO.log("DataSource is querying URL " + sUri, "info", this.toString());\r
2636     var oResponse = null;\r
2637     \r
2638     var oSelf = this;\r
2639     /*\r
2640      * Sets up ajax request callback\r
2641      *\r
2642      * @param {object} oReq          HTTPXMLRequest object\r
2643      * @private\r
2644      */\r
2645     var responseSuccess = function(oResp) {\r
2646         // Response ID does not match last made request ID.\r
2647         if(!oSelf._oConn || (oResp.tId != oSelf._oConn.tId)) {\r
2648             oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);\r
2649             YAHOO.log(YAHOO.widget.DataSource.ERROR_DATANULL, "error", this.toString());\r
2650             return;\r
2651         }\r
2652 //DEBUG\r
2653 /*YAHOO.log(oResp.responseXML.getElementsByTagName("Result"),'warn');\r
2654 for(var foo in oResp) {\r
2655     YAHOO.log(foo + ": "+oResp[foo],'warn');\r
2657 YAHOO.log('responseXML.xml: '+oResp.responseXML.xml,'warn');*/\r
2658         if(!isXML) {\r
2659             oResp = oResp.responseText;\r
2660         }\r
2661         else { \r
2662             oResp = oResp.responseXML;\r
2663         }\r
2664         if(oResp === null) {\r
2665             oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);\r
2666             YAHOO.log(YAHOO.widget.DataSource.ERROR_DATANULL, "error", oSelf.toString());\r
2667             return;\r
2668         }\r
2670         var aResults = oSelf.parseResponse(sQuery, oResp, oParent);\r
2671         var resultObj = {};\r
2672         resultObj.query = decodeURIComponent(sQuery);\r
2673         resultObj.results = aResults;\r
2674         if(aResults === null) {\r
2675             oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATAPARSE);\r
2676             YAHOO.log(YAHOO.widget.DataSource.ERROR_DATAPARSE, "error", oSelf.toString());\r
2677             aResults = [];\r
2678         }\r
2679         else {\r
2680             oSelf.getResultsEvent.fire(oSelf, oParent, sQuery, aResults);\r
2681             oSelf._addCacheElem(resultObj);\r
2682         }\r
2683         oCallbackFn(sQuery, aResults, oParent);\r
2684     };\r
2686     var responseFailure = function(oResp) {\r
2687         oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DS_XHR.ERROR_DATAXHR);\r
2688         YAHOO.log(YAHOO.widget.DS_XHR.ERROR_DATAXHR + ": " + oResp.statusText, "error", oSelf.toString());\r
2689         return;\r
2690     };\r
2691     \r
2692     var oCallback = {\r
2693         success:responseSuccess,\r
2694         failure:responseFailure\r
2695     };\r
2696     \r
2697     if(!isNaN(this.connTimeout) && this.connTimeout > 0) {\r
2698         oCallback.timeout = this.connTimeout;\r
2699     }\r
2700     \r
2701     if(this._oConn) {\r
2702         this.connMgr.abort(this._oConn);\r
2703     }\r
2704     \r
2705     oSelf._oConn = this.connMgr.asyncRequest("GET", sUri, oCallback, null);\r
2706 };\r
2708 /**\r
2709  * Parses raw response data into an array of result objects. The result data key\r
2710  * is always stashed in the [0] element of each result object. \r
2711  *\r
2712  * @method parseResponse\r
2713  * @param sQuery {String} Query string.\r
2714  * @param oResponse {Object} The raw response data to parse.\r
2715  * @param oParent {Object} The object instance that has requested data.\r
2716  * @returns {Object[]} Array of result objects.\r
2717  */\r
2718 YAHOO.widget.DS_XHR.prototype.parseResponse = function(sQuery, oResponse, oParent) {\r
2719     var aSchema = this.schema;\r
2720     var aResults = [];\r
2721     var bError = false;\r
2723     // Strip out comment at the end of results\r
2724     var nEnd = ((this.responseStripAfter !== "") && (oResponse.indexOf)) ?\r
2725         oResponse.indexOf(this.responseStripAfter) : -1;\r
2726     if(nEnd != -1) {\r
2727         oResponse = oResponse.substring(0,nEnd);\r
2728     }\r
2730     switch (this.responseType) {\r
2731         case YAHOO.widget.DS_XHR.TYPE_JSON:\r
2732             var jsonList;\r
2733             // Divert KHTML clients from JSON lib\r
2734             if(window.JSON && (navigator.userAgent.toLowerCase().indexOf('khtml')== -1)) {\r
2735                 // Use the JSON utility if available\r
2736                 var jsonObjParsed = JSON.parse(oResponse);\r
2737                 if(!jsonObjParsed) {\r
2738                     bError = true;\r
2739                     break;\r
2740                 }\r
2741                 else {\r
2742                     try {\r
2743                         // eval is necessary here since aSchema[0] is of unknown depth\r
2744                         jsonList = eval("jsonObjParsed." + aSchema[0]);\r
2745                     }\r
2746                     catch(e) {\r
2747                         bError = true;\r
2748                         break;\r
2749                    }\r
2750                 }\r
2751             }\r
2752             else {\r
2753                 // Parse the JSON response as a string\r
2754                 try {\r
2755                     // Trim leading spaces\r
2756                     while (oResponse.substring(0,1) == " ") {\r
2757                         oResponse = oResponse.substring(1, oResponse.length);\r
2758                     }\r
2760                     // Invalid JSON response\r
2761                     if(oResponse.indexOf("{") < 0) {\r
2762                         bError = true;\r
2763                         break;\r
2764                     }\r
2766                     // Empty (but not invalid) JSON response\r
2767                     if(oResponse.indexOf("{}") === 0) {\r
2768                         break;\r
2769                     }\r
2771                     // Turn the string into an object literal...\r
2772                     // ...eval is necessary here\r
2773                     var jsonObjRaw = eval("(" + oResponse + ")");\r
2774                     if(!jsonObjRaw) {\r
2775                         bError = true;\r
2776                         break;\r
2777                     }\r
2779                     // Grab the object member that contains an array of all reponses...\r
2780                     // ...eval is necessary here since aSchema[0] is of unknown depth\r
2781                     jsonList = eval("(jsonObjRaw." + aSchema[0]+")");\r
2782                 }\r
2783                 catch(e) {\r
2784                     bError = true;\r
2785                     break;\r
2786                }\r
2787             }\r
2789             if(!jsonList) {\r
2790                 bError = true;\r
2791                 break;\r
2792             }\r
2794             if(jsonList.constructor != Array) {\r
2795                 jsonList = [jsonList];\r
2796             }\r
2797             \r
2798             // Loop through the array of all responses...\r
2799             for(var i = jsonList.length-1; i >= 0 ; i--) {\r
2800                 var aResultItem = [];\r
2801                 var jsonResult = jsonList[i];\r
2802                 // ...and loop through each data field value of each response\r
2803                 for(var j = aSchema.length-1; j >= 1 ; j--) {\r
2804                     // ...and capture data into an array mapped according to the schema...\r
2805                     var dataFieldValue = jsonResult[aSchema[j]];\r
2806                     if(!dataFieldValue) {\r
2807                         dataFieldValue = "";\r
2808                     }\r
2809                     //YAHOO.log("data: " + i + " value:" +j+" = "+dataFieldValue,"debug",this.toString());\r
2810                     aResultItem.unshift(dataFieldValue);\r
2811                 }\r
2812                 // If schema isn't well defined, pass along the entire result object\r
2813                 if(aResultItem.length == 1) {\r
2814                     aResultItem.push(jsonResult);\r
2815                 }\r
2816                 // Capture the array of data field values in an array of results\r
2817                 aResults.unshift(aResultItem);\r
2818             }\r
2819             break;\r
2820         case YAHOO.widget.DS_XHR.TYPE_XML:\r
2821             // Get the collection of results\r
2822             var xmlList = oResponse.getElementsByTagName(aSchema[0]);\r
2823             if(!xmlList) {\r
2824                 bError = true;\r
2825                 break;\r
2826             }\r
2827             // Loop through each result\r
2828             for(var k = xmlList.length-1; k >= 0 ; k--) {\r
2829                 var result = xmlList.item(k);\r
2830                 //YAHOO.log("Result"+k+" is "+result.attributes.item(0).firstChild.nodeValue,"debug",this.toString());\r
2831                 var aFieldSet = [];\r
2832                 // Loop through each data field in each result using the schema\r
2833                 for(var m = aSchema.length-1; m >= 1 ; m--) {\r
2834                     //YAHOO.log(aSchema[m]+" is "+result.attributes.getNamedItem(aSchema[m]).firstChild.nodeValue);\r
2835                     var sValue = null;\r
2836                     // Values may be held in an attribute...\r
2837                     var xmlAttr = result.attributes.getNamedItem(aSchema[m]);\r
2838                     if(xmlAttr) {\r
2839                         sValue = xmlAttr.value;\r
2840                         //YAHOO.log("Attr value is "+sValue,"debug",this.toString());\r
2841                     }\r
2842                     // ...or in a node\r
2843                     else{\r
2844                         var xmlNode = result.getElementsByTagName(aSchema[m]);\r
2845                         if(xmlNode && xmlNode.item(0) && xmlNode.item(0).firstChild) {\r
2846                             sValue = xmlNode.item(0).firstChild.nodeValue;\r
2847                             //YAHOO.log("Node value is "+sValue,"debug",this.toString());\r
2848                         }\r
2849                         else {\r
2850                             sValue = "";\r
2851                             //YAHOO.log("Value not found","debug",this.toString());\r
2852                         }\r
2853                     }\r
2854                     // Capture the schema-mapped data field values into an array\r
2855                     aFieldSet.unshift(sValue);\r
2856                 }\r
2857                 // Capture each array of values into an array of results\r
2858                 aResults.unshift(aFieldSet);\r
2859             }\r
2860             break;\r
2861         case YAHOO.widget.DS_XHR.TYPE_FLAT:\r
2862             if(oResponse.length > 0) {\r
2863                 // Delete the last line delimiter at the end of the data if it exists\r
2864                 var newLength = oResponse.length-aSchema[0].length;\r
2865                 if(oResponse.substr(newLength) == aSchema[0]) {\r
2866                     oResponse = oResponse.substr(0, newLength);\r
2867                 }\r
2868                 var aRecords = oResponse.split(aSchema[0]);\r
2869                 for(var n = aRecords.length-1; n >= 0; n--) {\r
2870                     aResults[n] = aRecords[n].split(aSchema[1]);\r
2871                 }\r
2872             }\r
2873             break;\r
2874         default:\r
2875             break;\r
2876     }\r
2877     sQuery = null;\r
2878     oResponse = null;\r
2879     oParent = null;\r
2880     if(bError) {\r
2881         return null;\r
2882     }\r
2883     else {\r
2884         return aResults;\r
2885     }\r
2886 };            \r
2888 /////////////////////////////////////////////////////////////////////////////\r
2889 //\r
2890 // Private member variables\r
2891 //\r
2892 /////////////////////////////////////////////////////////////////////////////\r
2894 /**\r
2895  * XHR connection object.\r
2896  *\r
2897  * @property _oConn\r
2898  * @type Object\r
2899  * @private\r
2900  */\r
2901 YAHOO.widget.DS_XHR.prototype._oConn = null;\r
2904 /****************************************************************************/\r
2905 /****************************************************************************/\r
2906 /****************************************************************************/\r
2908 /**\r
2909  * Implementation of YAHOO.widget.DataSource using a native Javascript function as\r
2910  * its live data source.\r
2911  *  \r
2912  * @class DS_JSFunction\r
2913  * @constructor\r
2914  * @extends YAHOO.widget.DataSource\r
2915  * @param oFunction {HTMLFunction} In-memory Javascript function that returns query results as an array of objects.\r
2916  * @param oConfigs {Object} (optional) Object literal of config params.\r
2917  */\r
2918 YAHOO.widget.DS_JSFunction = function(oFunction, oConfigs) {\r
2919     // Set any config params passed in to override defaults\r
2920     if(typeof oConfigs == "object") {\r
2921         for(var sConfig in oConfigs) {\r
2922             this[sConfig] = oConfigs[sConfig];\r
2923         }\r
2924     }\r
2926     // Initialization sequence\r
2927     if(!oFunction  || (oFunction.constructor != Function)) {\r
2928         YAHOO.log("Could not instantiate JSFunction DataSource due to invalid arguments", "error", this.toString());\r
2929         return;\r
2930     }\r
2931     else {\r
2932         this.dataFunction = oFunction;\r
2933         this._init();\r
2934         YAHOO.log("JS Function DataSource initialized","info",this.toString());\r
2935     }\r
2936 };\r
2938 YAHOO.widget.DS_JSFunction.prototype = new YAHOO.widget.DataSource();\r
2940 /////////////////////////////////////////////////////////////////////////////\r
2941 //\r
2942 // Public member variables\r
2943 //\r
2944 /////////////////////////////////////////////////////////////////////////////\r
2946 /**\r
2947  * In-memory Javascript function that returns query results.\r
2948  *\r
2949  * @property dataFunction\r
2950  * @type HTMLFunction\r
2951  */\r
2952 YAHOO.widget.DS_JSFunction.prototype.dataFunction = null;\r
2954 /////////////////////////////////////////////////////////////////////////////\r
2955 //\r
2956 // Public methods\r
2957 //\r
2958 /////////////////////////////////////////////////////////////////////////////\r
2960 /**\r
2961  * Queries the live data source defined by function for results. Results are\r
2962  * passed back to a callback function.\r
2963  *  \r
2964  * @method doQuery\r
2965  * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.\r
2966  * @param sQuery {String} Query string.\r
2967  * @param oParent {Object} The object instance that has requested data.\r
2968  */\r
2969 YAHOO.widget.DS_JSFunction.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {\r
2970     var oFunction = this.dataFunction;\r
2971     var aResults = [];\r
2972     \r
2973     aResults = oFunction(sQuery);\r
2974     if(aResults === null) {\r
2975         this.dataErrorEvent.fire(this, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);\r
2976         YAHOO.log(YAHOO.widget.DataSource.ERROR_DATANULL, "error", this.toString());\r
2977         return;\r
2978     }\r
2979     \r
2980     var resultObj = {};\r
2981     resultObj.query = decodeURIComponent(sQuery);\r
2982     resultObj.results = aResults;\r
2983     this._addCacheElem(resultObj);\r
2984     \r
2985     this.getResultsEvent.fire(this, oParent, sQuery, aResults);\r
2986     oCallbackFn(sQuery, aResults, oParent);\r
2987     return;\r
2988 };\r
2990 /****************************************************************************/\r
2991 /****************************************************************************/\r
2992 /****************************************************************************/\r
2994 /**\r
2995  * Implementation of YAHOO.widget.DataSource using a native Javascript array as\r
2996  * its live data source.\r
2997  *\r
2998  * @class DS_JSArray\r
2999  * @constructor\r
3000  * @extends YAHOO.widget.DataSource\r
3001  * @param aData {String[]} In-memory Javascript array of simple string data.\r
3002  * @param oConfigs {Object} (optional) Object literal of config params.\r
3003  */\r
3004 YAHOO.widget.DS_JSArray = function(aData, oConfigs) {\r
3005     // Set any config params passed in to override defaults\r
3006     if(typeof oConfigs == "object") {\r
3007         for(var sConfig in oConfigs) {\r
3008             this[sConfig] = oConfigs[sConfig];\r
3009         }\r
3010     }\r
3012     // Initialization sequence\r
3013     if(!aData || (aData.constructor != Array)) {\r
3014         YAHOO.log("Could not instantiate JSArray DataSource due to invalid arguments", "error", this.toString());\r
3015         return;\r
3016     }\r
3017     else {\r
3018         this.data = aData;\r
3019         this._init();\r
3020         YAHOO.log("JS Array DataSource initialized","info",this.toString());\r
3021     }\r
3022 };\r
3024 YAHOO.widget.DS_JSArray.prototype = new YAHOO.widget.DataSource();\r
3026 /////////////////////////////////////////////////////////////////////////////\r
3027 //\r
3028 // Public member variables\r
3029 //\r
3030 /////////////////////////////////////////////////////////////////////////////\r
3032 /**\r
3033  * In-memory Javascript array of strings.\r
3034  *\r
3035  * @property data\r
3036  * @type Array\r
3037  */\r
3038 YAHOO.widget.DS_JSArray.prototype.data = null;\r
3040 /////////////////////////////////////////////////////////////////////////////\r
3041 //\r
3042 // Public methods\r
3043 //\r
3044 /////////////////////////////////////////////////////////////////////////////\r
3046 /**\r
3047  * Queries the live data source defined by data for results. Results are passed\r
3048  * back to a callback function.\r
3049  *\r
3050  * @method doQuery\r
3051  * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.\r
3052  * @param sQuery {String} Query string.\r
3053  * @param oParent {Object} The object instance that has requested data.\r
3054  */\r
3055 YAHOO.widget.DS_JSArray.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {\r
3056     var aData = this.data; // the array\r
3057     var aResults = []; // container for results\r
3058     var bMatchFound = false;\r
3059     var bMatchContains = this.queryMatchContains;\r
3060     if(sQuery) {\r
3061         if(!this.queryMatchCase) {\r
3062             sQuery = sQuery.toLowerCase();\r
3063         }\r
3065         // Loop through each element of the array...\r
3066         // which can be a string or an array of strings\r
3067         for(var i = aData.length-1; i >= 0; i--) {\r
3068             var aDataset = [];\r
3070             if(aData[i]) {\r
3071                 if(aData[i].constructor == String) {\r
3072                     aDataset[0] = aData[i];\r
3073                 }\r
3074                 else if(aData[i].constructor == Array) {\r
3075                     aDataset = aData[i];\r
3076                 }\r
3077             }\r
3079             if(aDataset[0] && (aDataset[0].constructor == String)) {\r
3080                 var sKeyIndex = (this.queryMatchCase) ?\r
3081                 encodeURIComponent(aDataset[0]).indexOf(sQuery):\r
3082                 encodeURIComponent(aDataset[0]).toLowerCase().indexOf(sQuery);\r
3084                 // A STARTSWITH match is when the query is found at the beginning of the key string...\r
3085                 if((!bMatchContains && (sKeyIndex === 0)) ||\r
3086                 // A CONTAINS match is when the query is found anywhere within the key string...\r
3087                 (bMatchContains && (sKeyIndex > -1))) {\r
3088                     // Stash a match into aResults[].\r
3089                     aResults.unshift(aDataset);\r
3090                 }\r
3091             }\r
3092         }\r
3093     }\r
3095     this.getResultsEvent.fire(this, oParent, sQuery, aResults);\r
3096     oCallbackFn(sQuery, aResults, oParent);\r
3097 };\r