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