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