Index: openacs-4/packages/ajaxhelper/www/resources/yui/autocomplete/autocomplete.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/ajaxhelper/www/resources/yui/autocomplete/autocomplete.js,v diff -u -r1.1 -r1.2 --- openacs-4/packages/ajaxhelper/www/resources/yui/autocomplete/autocomplete.js 21 Oct 2006 06:14:55 -0000 1.1 +++ openacs-4/packages/ajaxhelper/www/resources/yui/autocomplete/autocomplete.js 25 Dec 2006 16:40:00 -0000 1.2 @@ -6,3674 +6,2129 @@ http://developer.yahoo.com/yui/license.txt -version: 0.11.3 +version: 0.12.1 */ -/****************************************************************************/ + /** + * The AutoComplete control provides the front-end logic for text-entry suggestion and + * completion functionality. + * + * @module autocomplete + * @requires yahoo, dom, event, datasource + * @optional animation, connection, json + * @namespace YAHOO.widget + * @title AutoComplete Widget + */ -/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ -/****************************************************************************/ +/** + * The AutoComplete class provides the customizable functionality of a plug-and-play DHTML + * auto completion widget. Some key features: + * + * + * @class AutoComplete + * @constructor + * @param elInput {HTMLElement} DOM element reference of an input field. + * @param elInput {String} String ID of an input field. + * @param elContainer {HTMLElement} DOM element reference of an existing DIV. + * @param elContainer {String} String ID of an existing DIV. + * @param oDataSource {Object} Instance of YAHOO.widget.DataSource for query/results. + * @param oConfigs {Object} (optional) Object literal of configuration params. + */ +YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) { + if(elInput && elContainer && oDataSource) { + // Validate DataSource + if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) { + this.dataSource = oDataSource; + } + else { + return; + } - + // Validate input element + if(YAHOO.util.Dom.inDocument(elInput)) { + if(typeof elInput == "string") { + this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput; + this._oTextbox = document.getElementById(elInput); + } + else { + this._sName = (elInput.id) ? + "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id: + "instance" + YAHOO.widget.AutoComplete._nIndex; + this._oTextbox = elInput; + } + } + else { + return; + } -/** + // Validate container element + if(YAHOO.util.Dom.inDocument(elContainer)) { + if(typeof elContainer == "string") { + this._oContainer = document.getElementById(elContainer); + } + else { + this._oContainer = elContainer; + } + if(this._oContainer.style.display == "none") { + } + } + else { + return; + } - * Class providing the customizable functionality of a plug-and-play DHTML + // Set any config params passed in to override defaults + if (typeof oConfigs == "object") { + for(var sConfig in oConfigs) { + if (sConfig) { + this[sConfig] = oConfigs[sConfig]; + } + } + } - * auto complete widget. Some key features: + // Initialization sequence + this._initContainer(); + this._initProps(); + this._initList(); + this._initContainerHelpers(); - * +/** + * Number of characters that must be entered before querying for results. A negative value + * effectively turns off the widget. A value of 0 allows queries of null or empty string + * values. + * + * @property minQueryLength + * @type Number + * @default 1 + */ +YAHOO.widget.AutoComplete.prototype.minQueryLength = 1; - * +/** + * Maximum number of results to display in results container. + * + * @property maxResultsDisplayed + * @type Number + * @default 10 + */ +YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10; - * requires YAHOO.util.Dom Dom utility +/** + * Number of seconds to delay before submitting a query request. If a query + * request is received before a previous one has completed its delay, the + * previous request is cancelled and the new request is set to the delay. + * + * @property queryDelay + * @type Number + * @default 0.5 + */ +YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5; - * requires YAHOO.util.Event Event utility +/** + * Class name of a highlighted item within results container. + * + * @property highlighClassName + * @type String + * @default "yui-ac-highlight" + */ +YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight"; - * requires YAHOO.widget.DataSource Data source class +/** + * Class name of a pre-highlighted item within results container. + * + * @property prehighlightClassName + * @type String + */ +YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null; - * see YAHOO.util.Animation Animation utility +/** + * Query delimiter. A single character separator for multiple delimited + * selections. Multiple delimiter characteres may be defined as an array of + * strings. A null value or empty string indicates that query results cannot + * be delimited. This feature is not recommended if you need forceSelection to + * be true. + * + * @property delimChar + * @type String | String[] + */ +YAHOO.widget.AutoComplete.prototype.delimChar = null; - * see JSON JSON library +/** + * Whether or not the first item in results container should be automatically highlighted + * on expand. + * + * @property autoHighlight + * @type Boolean + * @default true + */ +YAHOO.widget.AutoComplete.prototype.autoHighlight = true; - * +/** + * Whether or not the input field should be automatically updated + * with the first query result as the user types, auto-selecting the substring + * that the user has not typed. + * + * @property typeAhead + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.typeAhead = false; - * @constructor +/** + * Whether or not to animate the expansion/collapse of the results container in the + * horizontal direction. + * + * @property animHoriz + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.animHoriz = false; - * @param {element | string} inputEl DOM element reference or string ID of the auto complete input field +/** + * Whether or not to animate the expansion/collapse of the results container in the + * vertical direction. + * + * @property animVert + * @type Boolean + * @default true + */ +YAHOO.widget.AutoComplete.prototype.animVert = true; - * @param {element | string} containerEl DOM element reference or string ID of the auto complete <div> +/** + * Speed of container expand/collapse animation, in seconds.. + * + * @property animSpeed + * @type Number + * @default 0.3 + */ +YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3; - * container +/** + * Whether or not to force the user's selection to match one of the query + * results. Enabling this feature essentially transforms the input field into a + * <select> field. This feature is not recommended with delimiter character(s) + * defined. + * + * @property forceSelection + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.forceSelection = false; - * @param {object} oDataSource Instance of YAHOO.widget.DataSource for query/results +/** + * Whether or not to allow browsers to cache user-typed input in the input + * field. Disabling this feature will prevent the widget from setting the + * autocomplete="off" on the input field. When autocomplete="off" + * and users click the back button after form submission, user-typed input can + * be prefilled by the browser from its cache. This caching of user input may + * not be desired for sensitive data, such as credit card numbers, in which + * case, implementers should consider setting allowBrowserAutocomplete to false. + * + * @property allowBrowserAutocomplete + * @type Boolean + * @default true + */ +YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true; - * @param {object} oConfigs Optional object literal of config params +/** + * Whether or not the results container should always be displayed. + * Enabling this feature displays the container when the widget is instantiated + * and prevents the toggling of the container to a collapsed state. + * + * @property alwaysShowContainer + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false; - */ +/** + * Whether or not to use an iFrame to layer over Windows form elements in + * IE. Set to true only when the results container will be on top of a + * <select> field in IE and thus exposed to the IE z-index bug (i.e., + * 5.5 < IE < 7). + * + * @property useIFrame + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.useIFrame = false; -YAHOO.widget.AutoComplete = function(inputEl,containerEl,oDataSource,oConfigs) { +/** + * Whether or not the results container should have a shadow. + * + * @property useShadow + * @type Boolean + * @default false + */ +YAHOO.widget.AutoComplete.prototype.useShadow = false; - if(inputEl && containerEl && oDataSource) { +///////////////////////////////////////////////////////////////////////////// +// +// Public methods +// +///////////////////////////////////////////////////////////////////////////// - // Validate data source + /** + * Public accessor to the unique name of the AutoComplete instance. + * + * @method toString + * @return {String} Unique name of the AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.toString = function() { + return "AutoComplete " + this._sName; +}; - if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) { + /** + * Returns true if container is in an expanded state, false otherwise. + * + * @method isContainerOpen + * @return {Boolean} Returns true if container is in an expanded state, false otherwise. + */ +YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() { + return this._bContainerOpen; +}; - this.dataSource = oDataSource; +/** + * Public accessor to the internal array of DOM <li> elements that + * display query results within the results container. + * + * @method getListItems + * @return {HTMLElement[]} Array of <li> elements within the results container. + */ +YAHOO.widget.AutoComplete.prototype.getListItems = function() { + return this._aListItems; +}; - } +/** + * Public accessor to the data held in an <li> element of the + * results container. + * + * @method getListItemData + * @return {Object | Array} Object or array of result data or null + */ +YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) { + if(oListItem._oResultData) { + return oListItem._oResultData; + } + else { + return false; + } +}; - else { +/** + * Sets HTML markup for the results container header. This markup will be + * inserted within a <div> tag with a class of "ac_hd". + * + * @method setHeader + * @param sHeader {String} HTML markup for results container header. + */ +YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) { + if(sHeader) { + if(this._oContainer._oContent._oHeader) { + this._oContainer._oContent._oHeader.innerHTML = sHeader; + this._oContainer._oContent._oHeader.style.display = "block"; + } + } + else { + this._oContainer._oContent._oHeader.innerHTML = ""; + this._oContainer._oContent._oHeader.style.display = "none"; + } +}; - return; +/** + * Sets HTML markup for the results container footer. This markup will be + * inserted within a <div> tag with a class of "ac_ft". + * + * @method setFooter + * @param sFooter {String} HTML markup for results container footer. + */ +YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) { + if(sFooter) { + if(this._oContainer._oContent._oFooter) { + this._oContainer._oContent._oFooter.innerHTML = sFooter; + this._oContainer._oContent._oFooter.style.display = "block"; + } + } + else { + this._oContainer._oContent._oFooter.innerHTML = ""; + this._oContainer._oContent._oFooter.style.display = "none"; + } +}; - } +/** + * Sets HTML markup for the results container body. This markup will be + * inserted within a <div> tag with a class of "ac_bd". + * + * @method setBody + * @param sHeader {String} HTML markup for results container body. + */ +YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) { + if(sBody) { + if(this._oContainer._oContent._oBody) { + this._oContainer._oContent._oBody.innerHTML = sBody; + this._oContainer._oContent._oBody.style.display = "block"; + this._oContainer._oContent.style.display = "block"; + } + } + else { + this._oContainer._oContent._oBody.innerHTML = ""; + this._oContainer._oContent.style.display = "none"; + } + this._maxResultsDisplayed = 0; +}; - +/** + * Overridable method that converts a result item object into HTML markup + * for display. Return data values are accessible via the oResultItem object, + * and the key return value will always be oResultItem[0]. Markup will be + * displayed within <li> element tags in the container. + * + * @method formatResult + * @param oResultItem {Object} Result item representing one query result. Data is held in an array. + * @param sQuery {String} The current query string. + * @return {String} HTML markup of formatted result data. + */ +YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) { + var sResult = oResultItem[0]; + if(sResult) { + return sResult; + } + else { + return ""; + } +}; - // Validate input element +/** + * Overridable method called before container expands allows implementers to access data + * and DOM elements. + * + * @method doBeforeExpandContainer + * @return {Boolean} Return true to continue expanding container, false to cancel the expand. + */ +YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oResultItem, sQuery) { + return true; +}; - if(YAHOO.util.Dom.inDocument(inputEl)) { +/** + * Makes query request to the DataSource. + * + * @method sendQuery + * @param sQuery {String} Query string. + */ +YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) { + this._sendQuery(sQuery); +}; - if(typeof inputEl == "string") { +///////////////////////////////////////////////////////////////////////////// +// +// Public events +// +///////////////////////////////////////////////////////////////////////////// - this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + inputEl; +/** + * Fired when the input field receives focus. + * + * @event textboxFocusEvent + * @param oSelf {Object} The AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null; - this._oTextbox = document.getElementById(inputEl); +/** + * Fired when the input field receives key input. + * + * @event textboxKeyEvent + * @param oSelf {Object} The AutoComplete instance. + * @param nKeycode {Number} The keycode number. + */ +YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null; - } +/** + * Fired when the AutoComplete instance makes a query to the DataSource. + * + * @event dataRequestEvent + * @param oSelf {Object} The AutoComplete instance. + * @param sQuery {String} The query string. + */ +YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null; - else { +/** + * Fired when the AutoComplete instance receives query results from the data + * source. + * + * @event dataReturnEvent + * @param oSelf {Object} The AutoComplete instance. + * @param sQuery {String} The query string. + * @param aResults {Array} Results array. + */ +YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null; - this._sName = (inputEl.id) ? +/** + * Fired when the AutoComplete instance does not receive query results from the + * DataSource due to an error. + * + * @event dataErrorEvent + * @param oSelf {Object} The AutoComplete instance. + * @param sQuery {String} The query string. + */ +YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null; - "instance" + YAHOO.widget.AutoComplete._nIndex + " " + inputEl.id: +/** + * Fired when the results container is expanded. + * + * @event containerExpandEvent + * @param oSelf {Object} The AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null; - "instance" + YAHOO.widget.AutoComplete._nIndex; +/** + * Fired when the input field has been prefilled by the type-ahead + * feature. + * + * @event typeAheadEvent + * @param oSelf {Object} The AutoComplete instance. + * @param sQuery {String} The query string. + * @param sPrefill {String} The prefill string. + */ +YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null; - this._oTextbox = inputEl; +/** + * Fired when result item has been moused over. + * + * @event itemMouseOverEvent + * @param oSelf {Object} The AutoComplete instance. + * @param elItem {HTMLElement} The <li> element item moused to. + */ +YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null; - } +/** + * Fired when result item has been moused out. + * + * @event itemMouseOutEvent + * @param oSelf {Object} The AutoComplete instance. + * @param elItem {HTMLElement} The <li> element item moused from. + */ +YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null; - } +/** + * Fired when result item has been arrowed to. + * + * @event itemArrowToEvent + * @param oSelf {Object} The AutoComplete instance. + * @param elItem {HTMLElement} The <li> element item arrowed to. + */ +YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null; - else { +/** + * Fired when result item has been arrowed away from. + * + * @event itemArrowFromEvent + * @param oSelf {Object} The AutoComplete instance. + * @param elItem {HTMLElement} The <li> element item arrowed from. + */ +YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null; - return; +/** + * Fired when an item is selected via mouse click, ENTER key, or TAB key. + * + * @event itemSelectEvent + * @param oSelf {Object} The AutoComplete instance. + * @param elItem {HTMLElement} The selected <li> element item. + * @param oData {Object} The data returned for the item, either as an object, + * or mapped from the schema into an array. + */ +YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null; - } +/** + * Fired when a user selection does not match any of the displayed result items. + * Note that this event may not behave as expected when delimiter characters + * have been defined. + * + * @event unmatchedItemSelectEvent + * @param oSelf {Object} The AutoComplete instance. + * @param sQuery {String} The user-typed query string. + */ +YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null; - +/** + * Fired if forceSelection is enabled and the user's input has been cleared + * because it did not match one of the returned query results. + * + * @event selectionEnforceEvent + * @param oSelf {Object} The AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null; - // Validate container element +/** + * Fired when the results container is collapsed. + * + * @event containerCollapseEvent + * @param oSelf {Object} The AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null; - if(YAHOO.util.Dom.inDocument(containerEl)) { +/** + * Fired when the input field loses focus. + * + * @event textboxBlurEvent + * @param oSelf {Object} The AutoComplete instance. + */ +YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null; - if(typeof containerEl == "string") { +///////////////////////////////////////////////////////////////////////////// +// +// Private member variables +// +///////////////////////////////////////////////////////////////////////////// - this._oContainer = document.getElementById(containerEl); +/** + * Internal class variable to index multiple AutoComplete instances. + * + * @property _nIndex + * @type Number + * @default 0 + * @private + */ +YAHOO.widget.AutoComplete._nIndex = 0; - } +/** + * Name of AutoComplete instance. + * + * @property _sName + * @type String + * @private + */ +YAHOO.widget.AutoComplete.prototype._sName = null; - else { +/** + * Text input field DOM element. + * + * @property _oTextbox + * @type HTMLElement + * @private + */ +YAHOO.widget.AutoComplete.prototype._oTextbox = null; - this._oContainer = containerEl; +/** + * Whether or not the input field is currently in focus. If query results come back + * but the user has already moved on, do not proceed with auto complete behavior. + * + * @property _bFocused + * @type Boolean + * @private + */ +YAHOO.widget.AutoComplete.prototype._bFocused = true; - } +/** + * Animation instance for container expand/collapse. + * + * @property _oAnim + * @type Boolean + * @private + */ +YAHOO.widget.AutoComplete.prototype._oAnim = null; - if(this._oContainer.style.display == "none") { +/** + * Container DOM element. + * + * @property _oContainer + * @type HTMLElement + * @private + */ +YAHOO.widget.AutoComplete.prototype._oContainer = null; - } +/** + * Whether or not the results container is currently open. + * + * @property _bContainerOpen + * @type Boolean + * @private + */ +YAHOO.widget.AutoComplete.prototype._bContainerOpen = false; - } +/** + * Whether or not the mouse is currently over the results + * container. This is necessary in order to prevent clicks on container items + * from being text input field blur events. + * + * @property _bOverContainer + * @type Boolean + * @private + */ +YAHOO.widget.AutoComplete.prototype._bOverContainer = false; - else { +/** + * Array of <li> elements references that contain query results within the + * results container. + * + * @property _aListItems + * @type Array + * @private + */ +YAHOO.widget.AutoComplete.prototype._aListItems = null; - return; +/** + * Number of <li> elements currently displayed in results container. + * + * @property _nDisplayedItems + * @type Number + * @private + */ +YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0; - } +/** + * Internal count of <li> elements displayed and hidden in results container. + * + * @property _maxResultsDisplayed + * @type Number + * @private + */ +YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0; - +/** + * Current query string + * + * @property _sCurQuery + * @type String + * @private + */ +YAHOO.widget.AutoComplete.prototype._sCurQuery = null; - // Set any config params passed in to override defaults +/** + * Past queries this session (for saving delimited queries). + * + * @property _sSavedQuery + * @type String + * @private + */ +YAHOO.widget.AutoComplete.prototype._sSavedQuery = null; - if (typeof oConfigs == "object") { +/** + * Pointer to the currently highlighted <li> element in the container. + * + * @property _oCurItem + * @type HTMLElement + * @private + */ +YAHOO.widget.AutoComplete.prototype._oCurItem = null; - for(var sConfig in oConfigs) { +/** + * Whether or not an item has been selected since the container was populated + * with results. Reset to false by _populateList, and set to true when item is + * selected. + * + * @property _bItemSelected + * @type Boolean + * @private + */ +YAHOO.widget.AutoComplete.prototype._bItemSelected = false; - if (sConfig) { +/** + * Key code of the last key pressed in textbox. + * + * @property _nKeyCode + * @type Number + * @private + */ +YAHOO.widget.AutoComplete.prototype._nKeyCode = null; - this[sConfig] = oConfigs[sConfig]; +/** + * Delay timeout ID. + * + * @property _nDelayID + * @type Number + * @private + */ +YAHOO.widget.AutoComplete.prototype._nDelayID = -1; - } +/** + * Src to iFrame used when useIFrame = true. Supports implementations over SSL + * as well. + * + * @property _iFrameSrc + * @type String + * @private + */ +YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;"; - } +/** + * For users typing via certain IMEs, queries must be triggered by intervals, + * since key events yet supported across all browsers for all IMEs. + * + * @property _queryInterval + * @type Object + * @private + */ +YAHOO.widget.AutoComplete.prototype._queryInterval = null; - } +/** + * Internal tracker to last known textbox value, used to determine whether or not + * to trigger a query via interval for certain IME users. + * + * @event _sLastTextboxValue + * @type String + * @private + */ +YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null; - +///////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +///////////////////////////////////////////////////////////////////////////// - // Initialization sequence +/** + * Updates and validates latest public config properties. + * + * @method __initProps + * @private + */ +YAHOO.widget.AutoComplete.prototype._initProps = function() { + // Correct any invalid values + var minQueryLength = this.minQueryLength; + if(isNaN(minQueryLength) || (minQueryLength < 1)) { + minQueryLength = 1; + } + var maxResultsDisplayed = this.maxResultsDisplayed; + if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) { + this.maxResultsDisplayed = 10; + } + var queryDelay = this.queryDelay; + if(isNaN(this.queryDelay) || (this.queryDelay < 0)) { + this.queryDelay = 0.5; + } + var aDelimChar = (this.delimChar) ? this.delimChar : null; + if(aDelimChar) { + if(typeof aDelimChar == "string") { + this.delimChar = [aDelimChar]; + } + else if(aDelimChar.constructor != Array) { + this.delimChar = null; + } + } + var animSpeed = this.animSpeed; + if((this.animHoriz || this.animVert) && YAHOO.util.Anim) { + if(isNaN(animSpeed) || (animSpeed < 0)) { + animSpeed = 0.3; + } + if(!this._oAnim ) { + oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed); + this._oAnim = oAnim; + } + else { + this._oAnim.duration = animSpeed; + } + } + if(this.forceSelection && this.delimChar) { + } +}; - this._initContainer(); +/** + * Initializes the results container helpers if they are enabled and do + * not exist + * + * @method _initContainerHelpers + * @private + */ +YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() { + if(this.useShadow && !this._oContainer._oShadow) { + var oShadow = document.createElement("div"); + oShadow.className = "yui-ac-shadow"; + this._oContainer._oShadow = this._oContainer.appendChild(oShadow); + } + if(this.useIFrame && !this._oContainer._oIFrame) { + var oIFrame = document.createElement("iframe"); + oIFrame.src = this._iFrameSrc; + oIFrame.frameBorder = 0; + oIFrame.scrolling = "no"; + oIFrame.style.position = "absolute"; + oIFrame.style.width = "100%"; + oIFrame.style.height = "100%"; + oIFrame.tabIndex = -1; + this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame); + } +}; - this._initProps(); +/** + * Initializes the results container once at object creation + * + * @method _initContainer + * @private + */ +YAHOO.widget.AutoComplete.prototype._initContainer = function() { + if(!this._oContainer._oContent) { + // The oContent div helps size the iframe and shadow properly + var oContent = document.createElement("div"); + oContent.className = "yui-ac-content"; + oContent.style.display = "none"; + this._oContainer._oContent = this._oContainer.appendChild(oContent); - this._initList(); + var oHeader = document.createElement("div"); + oHeader.className = "yui-ac-hd"; + oHeader.style.display = "none"; + this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader); - this._initContainerHelpers(); + var oBody = document.createElement("div"); + oBody.className = "yui-ac-bd"; + this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody); - + var oFooter = document.createElement("div"); + oFooter.className = "yui-ac-ft"; + oFooter.style.display = "none"; + this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter); + } + else { + } +}; - // Set up events +/** + * Clears out contents of container body and creates up to + * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an + * <ul> element. + * + * @method _initList + * @private + */ +YAHOO.widget.AutoComplete.prototype._initList = function() { + this._aListItems = []; + while(this._oContainer._oContent._oBody.hasChildNodes()) { + var oldListItems = this.getListItems(); + if(oldListItems) { + for(var oldi = oldListItems.length-1; oldi >= 0; i--) { + oldListItems[oldi] = null; + } + } + this._oContainer._oContent._oBody.innerHTML = ""; + } - var oSelf = this; + var oList = document.createElement("ul"); + oList = this._oContainer._oContent._oBody.appendChild(oList); + for(var i=0; i= 18 && nKeyCode <= 20) || // alt,pause/break,caps lock + (nKeyCode == 27) || // esc + (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end + (nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up + (nKeyCode == 40) || // down + (nKeyCode >= 44 && nKeyCode <= 45)) { // print screen,insert + return true; + } + return false; +}; - YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf); +/** + * Makes query request to the DataSource. + * + * @method _sendQuery + * @param sQuery {String} Query string. + * @private + */ +YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) { + // Widget has been effectively turned off + if(this.minQueryLength == -1) { + this._toggleContainer(false); + return; + } + // Delimiter has been enabled + var aDelimChar = (this.delimChar) ? this.delimChar : null; + if(aDelimChar) { + // Loop through all possible delimiters and find the latest one + // A " " may be a false positive if they are defined as delimiters AND + // are used to separate delimited queries + var nDelimIndex = -1; + for(var i = aDelimChar.length-1; i >= 0; i--) { + var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]); + if(nNewIndex > nDelimIndex) { + nDelimIndex = nNewIndex; + } + } + // If we think the last delimiter is a space (" "), make sure it is NOT + // a false positive by also checking the char directly before it + if(aDelimChar[i] == " ") { + for (var j = aDelimChar.length-1; j >= 0; j--) { + if(sQuery[nDelimIndex - 1] == aDelimChar[j]) { + nDelimIndex--; + break; + } + } + } + // A delimiter has been found so extract the latest query + if (nDelimIndex > -1) { + var nQueryStart = nDelimIndex + 1; + // Trim any white space from the beginning... + while(sQuery.charAt(nQueryStart) == " ") { + nQueryStart += 1; + } + // ...and save the rest of the string for later + this._sSavedQuery = sQuery.substring(0,nQueryStart); + // Here is the query itself + sQuery = sQuery.substr(nQueryStart); + } + else if(sQuery.indexOf(this._sSavedQuery) < 0){ + this._sSavedQuery = null; + } + } - YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf); + // Don't search queries that are too short + if (sQuery && (sQuery.length < this.minQueryLength) || (!sQuery && this.minQueryLength > 0)) { + if (this._nDelayID != -1) { + clearTimeout(this._nDelayID); + } + this._toggleContainer(false); + return; + } - YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf); + sQuery = encodeURIComponent(sQuery); + this._nDelayID = -1; // Reset timeout ID because request has been made + this.dataRequestEvent.fire(this, sQuery); + this.dataSource.getResults(this._populateList, sQuery, this); +}; - YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf); +/** + * Populates the array of <li> elements in the container with query + * results. This method is passed to YAHOO.widget.DataSource#getResults as a + * callback function so results from the DataSource instance are returned to the + * AutoComplete instance. + * + * @method _populateList + * @param sQuery {String} The query string. + * @param aResults {Array} An array of query result objects from the DataSource. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, aResults, oSelf) { + if(aResults === null) { + oSelf.dataErrorEvent.fire(oSelf, sQuery); + } + if (!oSelf._bFocused || !aResults) { + return; + } - YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf); + var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1); + var contentStyle = oSelf._oContainer._oContent.style; + contentStyle.width = (!isOpera) ? null : ""; + contentStyle.height = (!isOpera) ? null : ""; - YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf); + var sCurQuery = decodeURIComponent(sQuery); + oSelf._sCurQuery = sCurQuery; + oSelf._bItemSelected = false; - YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf); + if(oSelf._maxResultsDisplayed != oSelf.maxResultsDisplayed) { + oSelf._initList(); + } - YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf); + var nItems = Math.min(aResults.length,oSelf.maxResultsDisplayed); + oSelf._nDisplayedItems = nItems; + if (nItems > 0) { + oSelf._initContainerHelpers(); + var aItems = oSelf._aListItems; - if(oTextbox.form) { + // Fill items with data + for(var i = nItems-1; i >= 0; i--) { + var oItemi = aItems[i]; + var oResultItemi = aResults[i]; + oItemi.innerHTML = oSelf.formatResult(oResultItemi, sCurQuery); + oItemi.style.display = "list-item"; + oItemi._sResultKey = oResultItemi[0]; + oItemi._oResultData = oResultItemi; - YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf); + } - } + // Empty out remaining items if any + for(var j = aItems.length-1; j >= nItems ; j--) { + var oItemj = aItems[j]; + oItemj.innerHTML = null; + oItemj.style.display = "none"; + oItemj._sResultKey = null; + oItemj._oResultData = null; + } - + if(oSelf.autoHighlight) { + // Go to the first item + var oFirstItem = aItems[0]; + oSelf._toggleHighlight(oFirstItem,"to"); + oSelf.itemArrowToEvent.fire(oSelf, oFirstItem); + oSelf._typeAhead(oFirstItem,sQuery); + } + else { + oSelf._oCurItem = null; + } - // Custom events + // Expand the container + var ok = oSelf.doBeforeExpandContainer(oSelf._oTextbox, oSelf._oContainer, sQuery, aResults); + oSelf._toggleContainer(ok); + } + else { + oSelf._toggleContainer(false); + } + oSelf.dataReturnEvent.fire(oSelf, sQuery, aResults); +}; - this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this); +/** + * When forceSelection is true and the user attempts + * leave the text input box without selecting an item from the query results, + * the user selection is cleared. + * + * @method _clearSelection + * @private + */ +YAHOO.widget.AutoComplete.prototype._clearSelection = function() { + var sValue = this._oTextbox.value; + var sChar = (this.delimChar) ? this.delimChar[0] : null; + var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1; + if(nIndex > -1) { + this._oTextbox.value = sValue.substring(0,nIndex); + } + else { + this._oTextbox.value = ""; + } + this._sSavedQuery = this._oTextbox.value; - this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this); + // Fire custom event + this.selectionEnforceEvent.fire(this); +}; - this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this); +/** + * Whether or not user-typed value in the text input box matches any of the + * query results. + * + * @method _textMatchesOption + * @return {Boolean} True if user-input text matches a result, false otherwise. + * @private + */ +YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() { + var foundMatch = false; - this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this); + for(var i = this._nDisplayedItems-1; i >= 0 ; i--) { + var oItem = this._aListItems[i]; + var sMatch = oItem._sResultKey.toLowerCase(); + if (sMatch == this._sCurQuery.toLowerCase()) { + foundMatch = true; + break; + } + } + return(foundMatch); +}; - this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this); +/** + * Updates in the text input box with the first query result as the user types, + * selecting the substring that the user has not typed. + * + * @method _typeAhead + * @param oItem {HTMLElement} The <li> element item whose data populates the input field. + * @param sQuery {String} Query string. + * @private + */ +YAHOO.widget.AutoComplete.prototype._typeAhead = function(oItem, sQuery) { + // Don't update if turned off + if (!this.typeAhead || (this._nKeyCode == 8)) { + return; + } - this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this); + var oTextbox = this._oTextbox; + var sValue = this._oTextbox.value; // any saved queries plus what user has typed - this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this); + // Don't update with type-ahead if text selection is not supported + if(!oTextbox.setSelectionRange && !oTextbox.createTextRange) { + return; + } - this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this); + // Select the portion of text that the user has not typed + var nStart = sValue.length; + this._updateValue(oItem); + var nEnd = oTextbox.value.length; + this._selectText(oTextbox,nStart,nEnd); + var sPrefill = oTextbox.value.substr(nStart,nEnd); + this.typeAheadEvent.fire(this,sQuery,sPrefill); +}; - this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this); +/** + * Selects text in the input field. + * + * @method _selectText + * @param oTextbox {HTMLElement} Text input box element in which to select text. + * @param nStart {Number} Starting index of text string to select. + * @param nEnd {Number} Ending index of text selection. + * @private + */ +YAHOO.widget.AutoComplete.prototype._selectText = function(oTextbox, nStart, nEnd) { + if (oTextbox.setSelectionRange) { // For Mozilla + oTextbox.setSelectionRange(nStart,nEnd); + } + else if (oTextbox.createTextRange) { // For IE + var oTextRange = oTextbox.createTextRange(); + oTextRange.moveStart("character", nStart); + oTextRange.moveEnd("character", nEnd-oTextbox.value.length); + oTextRange.select(); + } + else { + oTextbox.select(); + } +}; - this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this); +/** + * Syncs results container with its helpers. + * + * @method _toggleContainerHelpers + * @param bShow {Boolean} True if container is expanded, false if collapsed + * @private + */ +YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) { + var bFireEvent = false; + var width = this._oContainer._oContent.offsetWidth + "px"; + var height = this._oContainer._oContent.offsetHeight + "px"; - this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this); + if(this.useIFrame && this._oContainer._oIFrame) { + bFireEvent = true; + if(bShow) { + this._oContainer._oIFrame.style.width = width; + this._oContainer._oIFrame.style.height = height; + } + else { + this._oContainer._oIFrame.style.width = 0; + this._oContainer._oIFrame.style.height = 0; + } + } + if(this.useShadow && this._oContainer._oShadow) { + bFireEvent = true; + if(bShow) { + this._oContainer._oShadow.style.width = width; + this._oContainer._oShadow.style.height = height; + } + else { + this._oContainer._oShadow.style.width = 0; + this._oContainer._oShadow.style.height = 0; + } + } +}; - this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this); +/** + * Animates expansion or collapse of the container. + * + * @method _toggleContainer + * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed + * @private + */ +YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) { + var oContainer = this._oContainer; - this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this); + // Implementer has container always open so don't mess with it + if(this.alwaysShowContainer && this._bContainerOpen) { + return; + } - this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this); + // Clear contents of container + if(!bShow) { + this._oContainer._oContent.scrollTop = 0; + var aItems = this._aListItems; - this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this); + if(aItems && (aItems.length > 0)) { + for(var i = aItems.length-1; i >= 0 ; i--) { + aItems[i].style.display = "none"; + } + } - this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this); + if (this._oCurItem) { + this._toggleHighlight(this._oCurItem,"from"); + } - + this._oCurItem = null; + this._nDisplayedItems = 0; + this._sCurQuery = null; + } - // Finish up + // Container is already closed + if (!bShow && !this._bContainerOpen) { + oContainer._oContent.style.display = "none"; + return; + } - oTextbox.setAttribute("autocomplete","off"); + // If animation is enabled... + var oAnim = this._oAnim; + if (oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) { + // If helpers need to be collapsed, do it right away... + // but if helpers need to be expanded, wait until after the container expands + if(!bShow) { + this._toggleContainerHelpers(bShow); + } - YAHOO.widget.AutoComplete._nIndex++; + if(oAnim.isAnimated()) { + oAnim.stop(); + } - } + // Clone container to grab current size offscreen + var oClone = oContainer._oContent.cloneNode(true); + oContainer.appendChild(oClone); + oClone.style.top = "-9000px"; + oClone.style.display = "block"; - // Required arguments were not found + // Current size of the container is the EXPANDED size + var wExp = oClone.offsetWidth; + var hExp = oClone.offsetHeight; - else { + // Calculate COLLAPSED sizes based on horiz and vert anim + var wColl = (this.animHoriz) ? 0 : wExp; + var hColl = (this.animVert) ? 0 : hExp; - } + // Set animation sizes + oAnim.attributes = (bShow) ? + {width: { to: wExp }, height: { to: hExp }} : + {width: { to: wColl}, height: { to: hColl }}; -}; + // If opening anew, set to a collapsed size... + if(bShow && !this._bContainerOpen) { + oContainer._oContent.style.width = wColl+"px"; + oContainer._oContent.style.height = hColl+"px"; + } + // Else, set it to its last known size. + else { + oContainer._oContent.style.width = wExp+"px"; + oContainer._oContent.style.height = hExp+"px"; + } - + oContainer.removeChild(oClone); + oClone = null; -/*************************************************************************** + var oSelf = this; + var onAnimComplete = function() { + // Finish the collapse + oAnim.onComplete.unsubscribeAll(); - * Public member variables + if(bShow) { + oSelf.containerExpandEvent.fire(oSelf); + } + else { + oContainer._oContent.style.display = "none"; + oSelf.containerCollapseEvent.fire(oSelf); + } + oSelf._toggleContainerHelpers(bShow); + }; - ***************************************************************************/ + // Display container and animate it + oContainer._oContent.style.display = "block"; + oAnim.onComplete.subscribe(onAnimComplete); + oAnim.animate(); + this._bContainerOpen = bShow; + } + // Else don't animate, just show or hide + else { + if(bShow) { + oContainer._oContent.style.display = "block"; + this.containerExpandEvent.fire(this); + } + else { + oContainer._oContent.style.display = "none"; + this.containerCollapseEvent.fire(this); + } + this._toggleContainerHelpers(bShow); + this._bContainerOpen = bShow; + } -/** +}; - * The data source object that encapsulates the data used for auto completion. +/** + * Toggles the highlight on or off for an item in the container, and also cleans + * up highlighting of any previous item. + * + * @method _toggleHighlight + * @param oNewItem {HTMLElement} The <li> element item to receive highlight behavior. + * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off. + * @private + */ +YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(oNewItem, sType) { + var sHighlight = this.highlightClassName; + if(this._oCurItem) { + // Remove highlight from old item + YAHOO.util.Dom.removeClass(this._oCurItem, sHighlight); + } - * This object should be an inherited object from YAHOO.widget.DataSource. + if((sType == "to") && sHighlight) { + // Apply highlight to new item + YAHOO.util.Dom.addClass(oNewItem, sHighlight); + this._oCurItem = oNewItem; + } +}; - * +/** + * Toggles the pre-highlight on or off for an item in the container. + * + * @method _togglePrehighlight + * @param oNewItem {HTMLElement} The <li> element item to receive highlight behavior. + * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off. + * @private + */ +YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(oNewItem, sType) { + if(oNewItem == this._oCurItem) { + return; + } - * @type object + var sPrehighlight = this.prehighlightClassName; + if((sType == "mouseover") && sPrehighlight) { + // Apply prehighlight to new item + YAHOO.util.Dom.addClass(oNewItem, sPrehighlight); + } + else { + // Remove prehighlight from old item + YAHOO.util.Dom.removeClass(oNewItem, sPrehighlight); + } +}; - */ +/** + * Updates the text input box value with selected query result. If a delimiter + * has been defined, then the value gets appended with the delimiter. + * + * @method _updateValue + * @param oItem {HTMLElement} The <li> element item with which to update the value. + * @private + */ +YAHOO.widget.AutoComplete.prototype._updateValue = function(oItem) { + var oTextbox = this._oTextbox; + var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null; + var sSavedQuery = this._sSavedQuery; + var sResultKey = oItem._sResultKey; + oTextbox.focus(); -YAHOO.widget.AutoComplete.prototype.dataSource = null; + // First clear text field + oTextbox.value = ""; + // Grab data to put into text field + if(sDelimChar) { + if(sSavedQuery) { + oTextbox.value = sSavedQuery; + } + oTextbox.value += sResultKey + sDelimChar; + if(sDelimChar != " ") { + oTextbox.value += " "; + } + } + else { oTextbox.value = sResultKey; } - + // scroll to bottom of textarea if necessary + if(oTextbox.type == "textarea") { + oTextbox.scrollTop = oTextbox.scrollHeight; + } -/** + // move cursor to end + var end = oTextbox.value.length; + this._selectText(oTextbox,end,end); - * Number of characters that must be entered before querying for results. + this._oCurItem = oItem; +}; - * Default: 1. +/** + * Selects a result item from the container + * + * @method _selectItem + * @param oItem {HTMLElement} The selected <li> element item. + * @private + */ +YAHOO.widget.AutoComplete.prototype._selectItem = function(oItem) { + this._bItemSelected = true; + this._updateValue(oItem); + this._cancelIntervalDetection(this); + this.itemSelectEvent.fire(this, oItem, oItem._oResultData); + this._toggleContainer(false); +}; - * +/** + * For values updated by type-ahead, the right arrow key jumps to the end + * of the textbox, otherwise the container is closed. + * + * @method _jumpSelection + * @private + */ +YAHOO.widget.AutoComplete.prototype._jumpSelection = function() { + if(!this.typeAhead) { + return; + } + else { + this._toggleContainer(false); + } +}; - * @type number +/** + * Triggered by up and down arrow keys, changes the current highlighted + * <li> element item. Scrolls container if necessary. + * + * @method _moveSelection + * @param nKeyCode {Number} Code of key pressed. + * @private + */ +YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) { + if(this._bContainerOpen) { + // Determine current item's id number + var oCurItem = this._oCurItem; + var nCurItemIndex = -1; - */ + if (oCurItem) { + nCurItemIndex = oCurItem._nItemIndex; + } -YAHOO.widget.AutoComplete.prototype.minQueryLength = 1; + var nNewItemIndex = (nKeyCode == 40) ? + (nCurItemIndex + 1) : (nCurItemIndex - 1); - + // Out of bounds + if (nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) { + return; + } -/** + if (oCurItem) { + // Unhighlight current item + this._toggleHighlight(oCurItem, "from"); + this.itemArrowFromEvent.fire(this, oCurItem); + } + if (nNewItemIndex == -1) { + // Go back to query (remove type-ahead string) + if(this.delimChar && this._sSavedQuery) { + if (!this._textMatchesOption()) { + this._oTextbox.value = this._sSavedQuery; + } + else { + this._oTextbox.value = this._sSavedQuery + this._sCurQuery; + } + } + else { + this._oTextbox.value = this._sCurQuery; + } + this._oCurItem = null; + return; + } + if (nNewItemIndex == -2) { + // Close container + this._toggleContainer(false); + return; + } - * Maximum number of results to display in auto complete container. Default: 10. + var oNewItem = this._aListItems[nNewItemIndex]; - * + // Scroll the container if necessary + var oContent = this._oContainer._oContent; + var scrollOn = ((YAHOO.util.Dom.getStyle(oContent,"overflow") == "auto") || + (YAHOO.util.Dom.getStyle(oContent,"overflowY") == "auto")); + if(scrollOn && (nNewItemIndex > -1) && + (nNewItemIndex < this._nDisplayedItems)) { + // User is keying down + if(nKeyCode == 40) { + // Bottom of selected item is below scroll area... + if((oNewItem.offsetTop+oNewItem.offsetHeight) > (oContent.scrollTop + oContent.offsetHeight)) { + // Set bottom of scroll area to bottom of selected item + oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight; + } + // Bottom of selected item is above scroll area... + else if((oNewItem.offsetTop+oNewItem.offsetHeight) < oContent.scrollTop) { + // Set top of selected item to top of scroll area + oContent.scrollTop = oNewItem.offsetTop; - * @type number + } + } + // User is keying up + else { + // Top of selected item is above scroll area + if(oNewItem.offsetTop < oContent.scrollTop) { + // Set top of scroll area to top of selected item + this._oContainer._oContent.scrollTop = oNewItem.offsetTop; + } + // Top of selected item is below scroll area + else if(oNewItem.offsetTop > (oContent.scrollTop + oContent.offsetHeight)) { + // Set bottom of selected item to bottom of scroll area + this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight; + } + } + } - */ + this._toggleHighlight(oNewItem, "to"); + this.itemArrowToEvent.fire(this, oNewItem); + if(this.typeAhead) { + this._updateValue(oNewItem); + } + } +}; -YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10; +///////////////////////////////////////////////////////////////////////////// +// +// Private event handlers +// +///////////////////////////////////////////////////////////////////////////// - +/** + * Handles <li> element mouseover events in the container. + * + * @method _onItemMouseover + * @param v {HTMLEvent} The mouseover event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onItemMouseover = function(v,oSelf) { + if(oSelf.prehighlightClassName) { + oSelf._togglePrehighlight(this,"mouseover"); + } + else { + oSelf._toggleHighlight(this,"to"); + } -/** + oSelf.itemMouseOverEvent.fire(oSelf, this); +}; - * Number of seconds to delay before submitting a query request. If a query +/** + * Handles <li> element mouseout events in the container. + * + * @method _onItemMouseout + * @param v {HTMLEvent} The mouseout event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onItemMouseout = function(v,oSelf) { + if(oSelf.prehighlightClassName) { + oSelf._togglePrehighlight(this,"mouseout"); + } + else { + oSelf._toggleHighlight(this,"from"); + } - * request is received before a previous one has completed its delay, the + oSelf.itemMouseOutEvent.fire(oSelf, this); +}; - * previous request is cancelled and the new request is set to the delay. +/** + * Handles <li> element click events in the container. + * + * @method _onItemMouseclick + * @param v {HTMLEvent} The click event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onItemMouseclick = function(v,oSelf) { + // In case item has not been moused over + oSelf._toggleHighlight(this,"to"); + oSelf._selectItem(this); +}; - * Default: 0.5. +/** + * Handles container mouseover events. + * + * @method _onContainerMouseover + * @param v {HTMLEvent} The mouseover event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) { + oSelf._bOverContainer = true; +}; - * +/** + * Handles container mouseout events. + * + * @method _onContainerMouseout + * @param v {HTMLEvent} The mouseout event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) { + oSelf._bOverContainer = false; + // If container is still active + if(oSelf._oCurItem) { + oSelf._toggleHighlight(oSelf._oCurItem,"to"); + } +}; - * @type number +/** + * Handles container scroll events. + * + * @method _onContainerScroll + * @param v {HTMLEvent} The scroll event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) { + oSelf._oTextbox.focus(); +}; - */ +/** + * Handles container resize events. + * + * @method _onContainerResize + * @param v {HTMLEvent} The resize event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) { + oSelf._toggleContainerHelpers(oSelf._bContainerOpen); +}; -YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5; +/** + * Handles textbox keydown events of functional keys, mainly for UI behavior. + * + * @method _onTextboxKeyDown + * @param v {HTMLEvent} The keydown event. + * @param oSelf {object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) { + var nKeyCode = v.keyCode; - + switch (nKeyCode) { + case 9: // tab + if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) { + if(oSelf._bContainerOpen) { + YAHOO.util.Event.stopEvent(v); + } + } + // select an item or clear out + if(oSelf._oCurItem) { + oSelf._selectItem(oSelf._oCurItem); + } + else { + oSelf._toggleContainer(false); + } + break; + case 13: // enter + if(oSelf._nKeyCode != nKeyCode) { + if(oSelf._bContainerOpen) { + YAHOO.util.Event.stopEvent(v); + } + } + if(oSelf._oCurItem) { + oSelf._selectItem(oSelf._oCurItem); + } + else { + oSelf._toggleContainer(false); + } + break; + case 27: // esc + oSelf._toggleContainer(false); + return; + case 39: // right + oSelf._jumpSelection(); + break; + case 38: // up + YAHOO.util.Event.stopEvent(v); + oSelf._moveSelection(nKeyCode); + break; + case 40: // down + YAHOO.util.Event.stopEvent(v); + oSelf._moveSelection(nKeyCode); + break; + default: + break; + } +}; -/** +/** + * Handles textbox keypress events. + * @method _onTextboxKeyPress + * @param v {HTMLEvent} The keypress event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) { + var nKeyCode = v.keyCode; - * Class name of a highlighted item within the auto complete container. + //Expose only to Mac browsers, where stopEvent is ineffective on keydown events (bug 790337) + var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1); + if(isMac) { + switch (nKeyCode) { + case 9: // tab + if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) { + if(oSelf._bContainerOpen) { + YAHOO.util.Event.stopEvent(v); + } + } + break; + case 13: // enter + if(oSelf._nKeyCode != nKeyCode) { + if(oSelf._bContainerOpen) { + YAHOO.util.Event.stopEvent(v); + } + } + break; + case 38: // up + case 40: // down + YAHOO.util.Event.stopEvent(v); + break; + default: + break; + } + } - * Default: "yui-ac-highlight". + //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948) + // Korean IME detected + else if(nKeyCode == 229) { + oSelf._queryInterval = setInterval(function() { oSelf._onIMEDetected(oSelf); },500); + } +}; - * +/** + * Handles textbox keyup events that trigger queries. + * + * @method _onTextboxKeyUp + * @param v {HTMLEvent} The keyup event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) { + // Check to see if any of the public properties have been updated + oSelf._initProps(); - * @type string + var nKeyCode = v.keyCode; + oSelf._nKeyCode = nKeyCode; + var sText = this.value; //string in textbox - */ + // Filter out chars that don't trigger queries + if (oSelf._isIgnoreKey(nKeyCode) || (sText.toLowerCase() == oSelf._sCurQuery)) { + return; + } + else { + oSelf.textboxKeyEvent.fire(oSelf, nKeyCode); + } -YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight"; + // Set timeout on the request + if (oSelf.queryDelay > 0) { + var nDelayID = + setTimeout(function(){oSelf._sendQuery(sText);},(oSelf.queryDelay * 1000)); - + if (oSelf._nDelayID != -1) { + clearTimeout(oSelf._nDelayID); + } -/** + oSelf._nDelayID = nDelayID; + } + else { + // No delay so send request immediately + oSelf._sendQuery(sText); + } +}; - * Class name of a pre-highlighted item within the auto complete container. +/** + * Handles text input box receiving focus. + * + * @method _onTextboxFocus + * @param v {HTMLEvent} The focus event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) { + oSelf._oTextbox.setAttribute("autocomplete","off"); + oSelf._bFocused = true; + oSelf.textboxFocusEvent.fire(oSelf); +}; - * Default: null. +/** + * Handles text input box losing focus. + * + * @method _onTextboxBlur + * @param v {HTMLEvent} The focus event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) { + // Don't treat as a blur if it was a selection via mouse click + if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) { + // Current query needs to be validated + if(!oSelf._bItemSelected) { + if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && !oSelf._textMatchesOption())) { + if(oSelf.forceSelection) { + oSelf._clearSelection(); + } + else { + oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery); + } + } + } - * + if(oSelf._bContainerOpen) { + oSelf._toggleContainer(false); + } + oSelf._cancelIntervalDetection(oSelf); + oSelf._bFocused = false; + oSelf.textboxBlurEvent.fire(oSelf); + } +}; - * @type string +/** + * Handles form submission event. + * + * @method _onFormSubmit + * @param v {HTMLEvent} The submit event. + * @param oSelf {Object} The AutoComplete instance. + * @private + */ +YAHOO.widget.AutoComplete.prototype._onFormSubmit = function(v,oSelf) { + if(oSelf.allowBrowserAutocomplete) { + oSelf._oTextbox.setAttribute("autocomplete","on"); + } + else { + oSelf._oTextbox.setAttribute("autocomplete","off"); + } +}; +/****************************************************************************/ - */ +/****************************************************************************/ -YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null; +/****************************************************************************/ /** - * Query delimiter. A single character separator for multiple delimited + * The DataSource classes manages sending a request and returning response from a live - * selections. Multiple delimiter characteres may be defined as an array of + * database. Supported data include local JavaScript arrays and objects and databases - * strings. A null value or empty string indicates that query results cannot + * accessible via XHR connections. Supported response formats include JavaScript arrays, - * be delimited. This feature is not recommended if you need forceSelection to + * JSON, XML, and flat-file textual data. - * be true. Default: null. - * - * @type string or array + * @class DataSource - */ + * @constructor -YAHOO.widget.AutoComplete.prototype.delimChar = null; - - - -/** - - * Whether or not the first item in the auto complete container should be - - * automatically highlighted on expand. Default: true. - - * - - * @type boolean - */ -YAHOO.widget.AutoComplete.prototype.autoHighlight = true; +YAHOO.widget.DataSource = function() { - + /* abstract class */ -/** - - * Whether or not the auto complete input field should be automatically updated - - * with the first query result as the user types, auto-selecting the substring - - * that the user has not typed. Default: false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.typeAhead = false; - - - -/** - - * Whether or not to animate the expansion/collapse of the auto complete - - * container in the horizontal direction. Default: false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.animHoriz = false; - - - -/** - - * Whether or not to animate the expansion/collapse of the auto complete - - * container in the vertical direction. Default: true. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.animVert = true; - - - -/** - - * Speed of container expand/collapse animation, in seconds. Default: 0.3. - - * - - * @type number - - */ - -YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3; - - - -/** - - * Whether or not to force the user's selection to match one of the query - - * results. Enabling this feature essentially transforms the auto complete form - - * input field into a <select> field. This feature is not recommended - - * with delimiter character(s) defined. Default: false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.forceSelection = false; - - - -/** - - * Whether or not to allow browsers to cache user-typed input in the input - - * field. Disabling this feature will prevent the widget from setting the - - * autocomplete="off" on the auto complete input field. When autocomplete="off" - - * and users click the back button after form submission, user-typed input can - - * be prefilled by the browser from its cache. This caching of user input may - - * not be desired for sensitive data, such as credit card numbers, in which - - * case, implementers should consider setting allowBrowserAutocomplete to false. - - * Default: true. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true; - - - -/** - - * Whether or not the auto complete container should always be displayed. - - * Enabling this feature prevents the toggling of the container to a collapsed - - * state. Default: false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false; - - - -/** - - * Whether or not to use an iFrame to layer over Windows form elements in - - * IE. Set to true only when the auto complete container will be on top of a - - * <select> field in IE and thus exposed to the IE z-index bug (i.e., - - * 5.5 < IE < 7). Default:false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.useIFrame = false; - - - -/** - - * Whether or not the auto complete container should have a shadow. Default:false. - - * - - * @type boolean - - */ - -YAHOO.widget.AutoComplete.prototype.useShadow = false; - - - -/*************************************************************************** - - * Public methods - - ***************************************************************************/ - - /** - - * Public accessor to the unique name of the auto complete instance. - - * - - * @return {string} Unique name of the auto complete instance - - */ - -YAHOO.widget.AutoComplete.prototype.toString = function() { - - return "AutoComplete " + this._sName; - }; -/** - - * Public accessor to the internal array of DOM <li> elements that - - * display query results within the auto complete container. - - * - - * @return {array} Array of <li> elements within the auto complete - - * container - - */ - -YAHOO.widget.AutoComplete.prototype.getListItems = function() { - - return this._aListItems; - -}; - -/** +///////////////////////////////////////////////////////////////////////////// - * Public accessor to the data held in an <li> element of the +// - * auto complete container. +// Public constants - * +// - * @return {object or array} Object or array of result data or null +///////////////////////////////////////////////////////////////////////////// - */ - -YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) { - - if(oListItem._oResultData) { - - return oListItem._oResultData; - - } - - else { - - return false; - - } - -}; - /** - * Sets HTML markup for the auto complete container header. This markup will be + * Error message for null data responses. - * inserted within a <div> tag with a class of "ac_hd". - * - * @param {string} sHeader HTML markup for container header + * @property ERROR_DATANULL - */ + * @type String -YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) { + * @static - if(sHeader) { + * @final - if(this._oContainer._oContent._oHeader) { - - this._oContainer._oContent._oHeader.innerHTML = sHeader; - - this._oContainer._oContent._oHeader.style.display = "block"; - - } - - } - - else { - - this._oContainer._oContent._oHeader.innerHTML = ""; - - this._oContainer._oContent._oHeader.style.display = "none"; - - } - -}; - - - -/** - - * Sets HTML markup for the auto complete container footer. This markup will be - - * inserted within a <div> tag with a class of "ac_ft". - - * - - * @param {string} sFooter HTML markup for container footer - */ -YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) { +YAHOO.widget.DataSource.ERROR_DATANULL = "Response data was null"; - if(sFooter) { - - if(this._oContainer._oContent._oFooter) { - - this._oContainer._oContent._oFooter.innerHTML = sFooter; - - this._oContainer._oContent._oFooter.style.display = "block"; - - } - - } - - else { - - this._oContainer._oContent._oFooter.innerHTML = ""; - - this._oContainer._oContent._oFooter.style.display = "none"; - - } - -}; - /** - * Sets HTML markup for the auto complete container body. This markup will be + * Error message for data responses with parsing errors. - * inserted within a <div> tag with a class of "ac_bd". - * - * @param {string} sHeader HTML markup for container body + * @property ERROR_DATAPARSE - */ + * @type String -YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) { + * @static - if(sBody) { + * @final - if(this._oContainer._oContent._oBody) { - - this._oContainer._oContent._oBody.innerHTML = sBody; - - this._oContainer._oContent._oBody.style.display = "block"; - - this._oContainer._oContent.style.display = "block"; - - } - - } - - else { - - this._oContainer._oContent._oBody.innerHTML = ""; - - this._oContainer._oContent.style.display = "none"; - - } - - this._maxResultsDisplayed = 0; - -}; - - - -/** - - * Overridable method that converts a result item object into HTML markup - - * for display. Return data values are accessible via the oResultItem object, - - * and the key return value will always be oResultItem[0]. Markup will be - - * displayed within <li> element tags in the container. - - * - - * @param {object} oResultItem Result item object representing one query result - - * @param {string} sQuery The current query string - - * @return {string} HTML markup of formatted result data - */ -YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) { +YAHOO.widget.DataSource.ERROR_DATAPARSE = "Response data could not be parsed"; - var sResult = oResultItem[0]; - - if(sResult) { - - return sResult; - - } - - else { - - return ""; - - } - -}; - -/** - - * Makes query request to the data source. - - * - - * @param {string} sQuery Query string. - - */ - -YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) { - - if(sQuery) { - - this._sendQuery(sQuery); - - } - - else { - - return; - - } - -}; - -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Events +// - ***************************************************************************/ +// Public member variables -/** +// - * Fired when the auto complete text input box receives focus. Subscribers +///////////////////////////////////////////////////////////////////////////// - * receive the following array:
- - * - args[0] The auto complete object instance - - */ - -YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null; - /** - * Fired when the auto complete text input box receives key input. Subscribers - - * receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The keycode number - - */ - -YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null; - - - -/** - - * Fired when the auto complete instance makes a query to the data source. - - * Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The query string - - */ - -YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null; - - - -/** - - * Fired when the auto complete instance receives query results from the data - - * source. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The query string - - * - args[2] Results array - - */ - -YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null; - - - -/** - - * Fired when the auto complete instance does not receive query results from the - - * data source due to an error. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The query string - - */ - -YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null; - - - -/** - - * Fired when the auto complete container is expanded. If alwaysShowContainer is - - * enabled, then containerExpandEvent will be fired when the container is - - * populated with results. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - */ - -YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null; - - - -/** - - * Fired when the auto complete textbox has been prefilled by the type-ahead - - * feature. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The query string - - * - args[2] The prefill string - - */ - -YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null; - - - -/** - - * Fired when result item has been moused over. Subscribers receive the following - - * array:
- - * - args[0] The auto complete object instance - - * - args[1] The <li> element item moused to - - */ - -YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null; - - - -/** - - * Fired when result item has been moused out. Subscribers receive the - - * following array:
- - * - args[0] The auto complete object instance - - * - args[1] The <li> element item moused from - - */ - -YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null; - - - -/** - - * Fired when result item has been arrowed to. Subscribers receive the following - - * array:
- - * - args[0] The auto complete object instance - - * - args[1] The <li> element item arrowed to - - */ - -YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null; - - - -/** - - * Fired when result item has been arrowed away from. Subscribers receive the - - * following array:
- - * - args[0] The auto complete object instance - - * - args[1] The <li> element item arrowed from - - */ - -YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null; - - - -/** - - * Fired when an item is selected via mouse click, ENTER key, or TAB key. - - * Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The selected <li> element item - - * - args[2] The data returned for the item, either as an object, or mapped from the schema into an array - - */ - -YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null; - - - -/** - - * Fired when an user selection does not match any of the displayed result items. - - * Note that this event may not behave as expected when delimiter characters - - * have been defined. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - * - args[1] The user selection - - */ - -YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null; - - - -/** - - * Fired if forceSelection is enabled and the user's input has been cleared - - * because it did not match one of the returned query results. Subscribers - - * receive the following array:
- - * - args[0] The auto complete object instance - - */ - -YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null; - - - -/** - - * Fired when the auto complete container is collapsed. If alwaysShowContainer is - - * enabled, then containerCollapseEvent will be fired when the container is - - * cleared of results. Subscribers receive the following array:
- - * - args[0] The auto complete object instance - - */ - -YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null; - - - -/** - - * Fired when the auto complete text input box loses focus. Subscribers receive - - * an array of the following array:
- - * - args[0] The auto complete object instance - - */ - -YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null; - - - -/*************************************************************************** - - * Private member variables - - ***************************************************************************/ - -/** - - * Internal class variable to index multiple auto complete instances. - - * - - * @type number - - * @private - - */ - -YAHOO.widget.AutoComplete._nIndex = 0; - - - -/** - - * Name of auto complete instance. - - * - - * @type string - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._sName = null; - - - -/** - - * Text input box DOM element. - - * - - * @type object - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._oTextbox = null; - - - -/** - - * Whether or not the textbox is currently in focus. If query results come back - - * but the user has already moved on, do not proceed with auto complete behavior. - - * - - * @type boolean - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._bFocused = true; - - - -/** - - * Animation instance for container expand/collapse. - - * - - * @type boolean - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._oAnim = null; - - - -/** - - * Container DOM element. - - * - - * @type object - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._oContainer = null; - - - -/** - - * Whether or not the auto complete container is currently open. - - * - - * @type boolean - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._bContainerOpen = false; - - - -/** - - * Whether or not the mouse is currently over the auto complete - - * container. This is necessary in order to prevent clicks on container items - - * from being text input box blur events. - - * - - * @type boolean - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._bOverContainer = false; - - - -/** - - * Array of <li> elements references that contain query results within the - - * auto complete container. - - * - - * @type array - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._aListItems = null; - - - -/** - - * Number of <li> elements currently displayed in auto complete container. - - * - - * @type number - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0; - - - -/** - - * Internal count of <li> elements displayed and hidden in auto complete container. - - * - - * @type number - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0; - - - -/** - - * Current query string - - * - - * @type string - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._sCurQuery = null; - - - -/** - - * Past queries this session (for saving delimited queries). - - * - - * @type string - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._sSavedQuery = null; - - - -/** - - * Pointer to the currently highlighted <li> element in the container. - - * - - * @type object - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._oCurItem = null; - - - -/** - - * Whether or not an item has been selected since the container was populated - - * with results. Reset to false by _populateList, and set to true when item is - - * selected. - - * - - * @type boolean - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._bItemSelected = false; - - - -/** - - * Key code of the last key pressed in textbox. - - * - - * @type number - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._nKeyCode = null; - - - -/** - - * Delay timeout ID. - - * - - * @type number - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._nDelayID = -1; - - - -/** - - * Src to iFrame used when useIFrame = true. Supports implementations over SSL - - * as well. - - * - - * @type string - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;"; - - - -/*************************************************************************** - - * Private methods - - ***************************************************************************/ - -/** - - * Updates and validates latest public config properties. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._initProps = function() { - - // Correct any invalid values - - var minQueryLength = this.minQueryLength; - - if(isNaN(minQueryLength) || (minQueryLength < 1)) { - - minQueryLength = 1; - - } - - var maxResultsDisplayed = this.maxResultsDisplayed; - - if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) { - - this.maxResultsDisplayed = 10; - - } - - var queryDelay = this.queryDelay; - - if(isNaN(this.queryDelay) || (this.queryDelay < 0)) { - - this.queryDelay = 0.5; - - } - - var aDelimChar = (this.delimChar) ? this.delimChar : null; - - if(aDelimChar) { - - if(typeof aDelimChar == "string") { - - this.delimChar = [aDelimChar]; - - } - - else if(aDelimChar.constructor != Array) { - - this.delimChar = null; - - } - - } - - var animSpeed = this.animSpeed; - - if((this.animHoriz || this.animVert) && YAHOO.util.Anim) { - - if(isNaN(animSpeed) || (animSpeed < 0)) { - - animSpeed = 0.3; - - } - - if(!this._oAnim ) { - - oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed); - - this._oAnim = oAnim; - - } - - else { - - this._oAnim.duration = animSpeed; - - } - - } - - if(this.forceSelection && this.delimChar) { - - } - - if(this.alwaysShowContainer && (this.useShadow || this.useIFrame)) { - - } - - - - if(this.alwaysShowContainer) { - - this._bContainerOpen = true; - - } - -}; - - - -/** - - * Initializes the auto complete container helpers if they are enabled and do - - * not exist - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() { - - if(this.useShadow && !this._oContainer._oShadow) { - - var oShadow = document.createElement("div"); - - oShadow.className = "yui-ac-shadow"; - - this._oContainer._oShadow = this._oContainer.appendChild(oShadow); - - } - - if(this.useIFrame && !this._oContainer._oIFrame) { - - var oIFrame = document.createElement("iframe"); - - oIFrame.src = this._iFrameSrc; - - oIFrame.frameBorder = 0; - - oIFrame.scrolling = "no"; - - oIFrame.style.position = "absolute"; - - oIFrame.style.width = "100%"; - - oIFrame.style.height = "100%"; - - this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame); - - } - -}; - - - -/** - - * Initializes the auto complete container once at object creation - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._initContainer = function() { - - if(!this._oContainer._oContent) { - - // The oContent div helps size the iframe and shadow properly - - var oContent = document.createElement("div"); - - oContent.className = "yui-ac-content"; - - oContent.style.display = "none"; - - this._oContainer._oContent = this._oContainer.appendChild(oContent); - - - - var oHeader = document.createElement("div"); - - oHeader.className = "yui-ac-hd"; - - oHeader.style.display = "none"; - - this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader); - - - - var oBody = document.createElement("div"); - - oBody.className = "yui-ac-bd"; - - this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody); - - - - var oFooter = document.createElement("div"); - - oFooter.className = "yui-ac-ft"; - - oFooter.style.display = "none"; - - this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter); - - } - - else { - - } - -}; - - - -/** - - * Clears out contents of container body and creates up to - - * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an - - * <ul> element. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._initList = function() { - - this._aListItems = []; - - while(this._oContainer._oContent._oBody.hasChildNodes()) { - - var oldListItems = this.getListItems(); - - if(oldListItems) { - - for(var oldi = oldListItems.length-1; oldi >= 0; i--) { - - oldListItems[oldi] = null; - - } - - } - - this._oContainer._oContent._oBody.innerHTML = ""; - - } - - - - var oList = document.createElement("ul"); - - oList = this._oContainer._oContent._oBody.appendChild(oList); - - for(var i=0; i 0) { - - var nDelayID = - - setTimeout(function(){oSelf._sendQuery(sText);},(oSelf.queryDelay * 1000)); - - - - if (oSelf._nDelayID != -1) { - - clearTimeout(oSelf._nDelayID); - - } - - - - oSelf._nDelayID = nDelayID; - - } - - else { - - // No delay so send request immediately - - oSelf._sendQuery(sText); - - } - -}; - - - -/** - - * Whether or not key is functional or should be ignored. Note that the right - - * arrow key is NOT an ignored key since it triggers queries for certain intl - - * charsets. - - * - - * @param {number} nKeycode Code of key pressed - - * @return {boolean} Whether or not to be ignore key - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) { - - if ((nKeyCode == 9) || (nKeyCode == 13) || // tab, enter - - (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl - - (nKeyCode >= 18 && nKeyCode <= 20) || // alt,pause/break,caps lock - - (nKeyCode == 27) || // esc - - (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end - - (nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up - - (nKeyCode == 40) || // down - - (nKeyCode >= 44 && nKeyCode <= 45)) { // print screen,insert - - return true; - - } - - return false; - -}; - - - -/** - - * Handles text input box receiving focus. - - * - - * @param {event} v The focus event - - * @param {object} oSelf The auto complete instance - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) { - - oSelf._oTextbox.setAttribute("autocomplete","off"); - - oSelf._bFocused = true; - - oSelf.textboxFocusEvent.fire(oSelf); - -}; - - - -/** - - * Handles text input box losing focus. - - * - - * @param {event} v The focus event - - * @param {object} oSelf The auto complete instance - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) { - - // Don't treat as a blur if it was a selection via mouse click - - if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) { - - // Current query needs to be validated - - if(!oSelf._bItemSelected) { - - if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && !oSelf._textMatchesOption())) { - - if(oSelf.forceSelection) { - - oSelf._clearSelection(); - - } - - else { - - oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery); - - } - - } - - } - - - - if(oSelf._bContainerOpen) { - - oSelf._clearList(); - - } - - oSelf._bFocused = false; - - oSelf.textboxBlurEvent.fire(oSelf); - - } - -}; - - - -/** - - * Handles form submission event. - - * - - * @param {event} v The submit event - - * @param {object} oSelf The auto complete instance - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._onFormSubmit = function(v,oSelf) { - - if(oSelf.allowBrowserAutocomplete) { - - oSelf._oTextbox.setAttribute("autocomplete","on"); - - } - - else { - - oSelf._oTextbox.setAttribute("autocomplete","off"); - - } - -}; - - - -/** - - * Makes query request to the data source. - - * - - * @param {string} sQuery Query string. - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) { - - // Delimiter has been enabled - - var aDelimChar = (this.delimChar) ? this.delimChar : null; - - if(aDelimChar) { - - // Loop through all possible delimiters and find the latest one - - // A " " may be a false positive if they are defined as delimiters AND - - // are used to separate delimited queries - - var nDelimIndex = -1; - - for(var i = aDelimChar.length-1; i >= 0; i--) { - - var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]); - - if(nNewIndex > nDelimIndex) { - - nDelimIndex = nNewIndex; - - } - - } - - // If we think the last delimiter is a space (" "), make sure it is NOT - - // a false positive by also checking the char directly before it - - if(aDelimChar[i] == " ") { - - for (var j = aDelimChar.length-1; j >= 0; j--) { - - if(sQuery[nDelimIndex - 1] == aDelimChar[j]) { - - nDelimIndex--; - - break; - - } - - } - - } - - // A delimiter has been found so extract the latest query - - if (nDelimIndex > -1) { - - var nQueryStart = nDelimIndex + 1; - - // Trim any white space from the beginning... - - while(sQuery.charAt(nQueryStart) == " ") { - - nQueryStart += 1; - - } - - // ...and save the rest of the string for later - - this._sSavedQuery = sQuery.substring(0,nQueryStart); - - // Here is the query itself - - sQuery = sQuery.substr(nQueryStart); - - } - - else if(sQuery.indexOf(this._sSavedQuery) < 0){ - - this._sSavedQuery = null; - - } - - } - - - - // Don't search queries that are too short - - if (sQuery.length < this.minQueryLength) { - - if (this._nDelayID != -1) { - - clearTimeout(this._nDelayID); - - } - - this._clearList(); - - return; - - } - - - - sQuery = encodeURIComponent(sQuery); - - this._nDelayID = -1; // Reset timeout ID because request has been made - - this.dataRequestEvent.fire(this, sQuery); - - this.dataSource.getResults(this._populateList, sQuery, this); - -}; - - - -/** - - * Hides all visuals related to the array of <li> elements in the container. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._clearList = function() { - - this._oContainer._oContent.scrollTop = 0; - - var aItems = this._aListItems; - - - - if(aItems && (aItems.length > 0)) { - - for(var i = aItems.length-1; i >= 0 ; i--) { - - aItems[i].style.display = "none"; - - } - - } - - - - if (this._oCurItem) { - - this._toggleHighlight(this._oCurItem,"from"); - - } - - - - this._oCurItem = null; - - this._nDisplayedItems = 0; - - this._sCurQuery = null; - - this._toggleContainer(false); - -}; - - - -/** - - * Populates the array of <li> elements in the container with query - - * results. This method is passed to YAHOO.widget.DataSource#getResults as a - - * callback function so results from the datasource are returned to the - - * auto complete instance. - - * - - * @param {string} sQuery The query string - - * @param {object} aResults An array of query result objects from the data source - - * @param {string} oSelf The auto complete instance - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, aResults, oSelf) { - - if(aResults === null) { - - oSelf.dataErrorEvent.fire(oSelf, sQuery); - - } - - if (!oSelf._bFocused || !aResults) { - - return; - - } - - - - var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1); - - var contentStyle = oSelf._oContainer._oContent.style; - - contentStyle.width = (!isOpera) ? null : ""; - - contentStyle.height = (!isOpera) ? null : ""; - - - - var sCurQuery = decodeURIComponent(sQuery); - - oSelf._sCurQuery = sCurQuery; - - oSelf._bItemSelected = false; - - - - if(oSelf._maxResultsDisplayed != oSelf.maxResultsDisplayed) { - - oSelf._initList(); - - } - - - - var nItems = Math.min(aResults.length,oSelf.maxResultsDisplayed); - - oSelf._nDisplayedItems = nItems; - - if (nItems > 0) { - - oSelf._initContainerHelpers(); - - var aItems = oSelf._aListItems; - - - - // Fill items with data - - for(var i = nItems-1; i >= 0; i--) { - - var oItemi = aItems[i]; - - var oResultItemi = aResults[i]; - - oItemi.innerHTML = oSelf.formatResult(oResultItemi, sCurQuery); - - oItemi.style.display = "list-item"; - - oItemi._sResultKey = oResultItemi[0]; - - oItemi._oResultData = oResultItemi; - - - - } - - - - // Empty out remaining items if any - - for(var j = aItems.length-1; j >= nItems ; j--) { - - var oItemj = aItems[j]; - - oItemj.innerHTML = null; - - oItemj.style.display = "none"; - - oItemj._sResultKey = null; - - oItemj._oResultData = null; - - } - - - - if(oSelf.autoHighlight) { - - // Go to the first item - - var oFirstItem = aItems[0]; - - oSelf._toggleHighlight(oFirstItem,"to"); - - oSelf.itemArrowToEvent.fire(oSelf, oFirstItem); - - oSelf._typeAhead(oFirstItem,sQuery); - - } - - else { - - oSelf._oCurItem = null; - - } - - - - // Expand the container - - oSelf._toggleContainer(true); - - } - - else { - - oSelf._clearList(); - - } - - oSelf.dataReturnEvent.fire(oSelf, sQuery, aResults); - -}; - - - -/** - - * When YAHOO.widget.AutoComplete#bForceSelection is true and the user attempts - - * leave the text input box without selecting an item from the query results, - - * the user selection is cleared. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._clearSelection = function() { - - var sValue = this._oTextbox.value; - - var sChar = (this.delimChar) ? this.delimChar[0] : null; - - var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1; - - if(nIndex > -1) { - - this._oTextbox.value = sValue.substring(0,nIndex); - - } - - else { - - this._oTextbox.value = ""; - - } - - this._sSavedQuery = this._oTextbox.value; - - - - // Fire custom event - - this.selectionEnforceEvent.fire(this); - -}; - - - -/** - - * Whether or not user-typed value in the text input box matches any of the - - * query results. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() { - - var foundMatch = false; - - - - for(var i = this._nDisplayedItems-1; i >= 0 ; i--) { - - var oItem = this._aListItems[i]; - - var sMatch = oItem._sResultKey.toLowerCase(); - - if (sMatch == this._sCurQuery.toLowerCase()) { - - foundMatch = true; - - break; - - } - - } - - return(foundMatch); - -}; - - - -/** - - * Updates in the text input box with the first query result as the user types, - - * selecting the substring that the user has not typed. - - * - - * @param {object} oItem The <li> element item whose data populates the input field - - * @param {string} sQuery Query string - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._typeAhead = function(oItem, sQuery) { - - // Don't update if turned off - - if (!this.typeAhead) { - - return; - - } - - - - var oTextbox = this._oTextbox; - - var sValue = this._oTextbox.value; // any saved queries plus what user has typed - - - - // Don't update with type-ahead if text selection is not supported - - if(!oTextbox.setSelectionRange && !oTextbox.createTextRange) { - - return; - - } - - - - // Select the portion of text that the user has not typed - - var nStart = sValue.length; - - this._updateValue(oItem); - - var nEnd = oTextbox.value.length; - - this._selectText(oTextbox,nStart,nEnd); - - var sPrefill = oTextbox.value.substr(nStart,nEnd); - - this.typeAheadEvent.fire(this,sQuery,sPrefill); - -}; - - - -/** - - * Selects text in a text input box. - - * - - * @param {object} oTextbox Text input box element in which to select text - - * @param {number} nStart Starting index of text string to select - - * @param {number} nEnd Ending index of text selection - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._selectText = function(oTextbox, nStart, nEnd) { - - if (oTextbox.setSelectionRange) { // For Mozilla - - oTextbox.setSelectionRange(nStart,nEnd); - - } - - else if (oTextbox.createTextRange) { // For IE - - var oTextRange = oTextbox.createTextRange(); - - oTextRange.moveStart("character", nStart); - - oTextRange.moveEnd("character", nEnd-oTextbox.value.length); - - oTextRange.select(); - - } - - else { - - oTextbox.select(); - - } - -}; - - - -/** - - * Syncs auto complete container with its helpers. - - * - - * @param {boolean} bShow True if container is expanded, false if collapsed - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) { - - var bFireEvent = false; - - var width = this._oContainer._oContent.offsetWidth + "px"; - - var height = this._oContainer._oContent.offsetHeight + "px"; - - - - if(this.useIFrame && this._oContainer._oIFrame) { - - bFireEvent = true; - - if(this.alwaysShowContainer || bShow) { - - this._oContainer._oIFrame.style.width = width; - - this._oContainer._oIFrame.style.height = height; - - } - - else { - - this._oContainer._oIFrame.style.width = 0; - - this._oContainer._oIFrame.style.height = 0; - - } - - } - - if(this.useShadow && this._oContainer._oShadow) { - - bFireEvent = true; - - if(this.alwaysShowContainer || bShow) { - - this._oContainer._oShadow.style.width = width; - - this._oContainer._oShadow.style.height = height; - - } - - else { - - this._oContainer._oShadow.style.width = 0; - - this._oContainer._oShadow.style.height = 0; - - } - - } - -}; - - - -/** - - * Animates expansion or collapse of the container. - - * - - * @param {boolean} bShow True if container should be expanded, false if - - * container should be collapsed - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) { - - // Implementer has container always open so don't mess with it - - if(this.alwaysShowContainer) { - - // Fire these events to give implementers a hook into the container - - // being populated and being emptied - - if(bShow) { - - this.containerExpandEvent.fire(this); - - } - - else { - - this.containerCollapseEvent.fire(this); - - } - - this._bContainerOpen = bShow; - - return; - - } - - - - var oContainer = this._oContainer; - - // Container is already closed - - if (!bShow && !this._bContainerOpen) { - - oContainer._oContent.style.display = "none"; - - return; - - } - - - - // If animation is enabled... - - var oAnim = this._oAnim; - - if (oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) { - - // If helpers need to be collapsed, do it right away... - - // but if helpers need to be expanded, wait until after the container expands - - if(!bShow) { - - this._toggleContainerHelpers(bShow); - - } - - - - if(oAnim.isAnimated()) { - - oAnim.stop(); - - } - - - - // Clone container to grab current size offscreen - - var oClone = oContainer._oContent.cloneNode(true); - - oContainer.appendChild(oClone); - - oClone.style.top = "-9000px"; - - oClone.style.display = "block"; - - - - // Current size of the container is the EXPANDED size - - var wExp = oClone.offsetWidth; - - var hExp = oClone.offsetHeight; - - - - // Calculate COLLAPSED sizes based on horiz and vert anim - - var wColl = (this.animHoriz) ? 0 : wExp; - - var hColl = (this.animVert) ? 0 : hExp; - - - - // Set animation sizes - - oAnim.attributes = (bShow) ? - - {width: { to: wExp }, height: { to: hExp }} : - - {width: { to: wColl}, height: { to: hColl }}; - - - - // If opening anew, set to a collapsed size... - - if(bShow && !this._bContainerOpen) { - - oContainer._oContent.style.width = wColl+"px"; - - oContainer._oContent.style.height = hColl+"px"; - - } - - // Else, set it to its last known size. - - else { - - oContainer._oContent.style.width = wExp+"px"; - - oContainer._oContent.style.height = hExp+"px"; - - } - - - - oContainer.removeChild(oClone); - - oClone = null; - - - - var oSelf = this; - - var onAnimComplete = function() { - - // Finish the collapse - - oAnim.onComplete.unsubscribeAll(); - - - - if(bShow) { - - oSelf.containerExpandEvent.fire(oSelf); - - } - - else { - - oContainer._oContent.style.display = "none"; - - oSelf.containerCollapseEvent.fire(oSelf); - - } - - oSelf._toggleContainerHelpers(bShow); - - }; - - - - // Display container and animate it - - oContainer._oContent.style.display = "block"; - - oAnim.onComplete.subscribe(onAnimComplete); - - oAnim.animate(); - - this._bContainerOpen = bShow; - - } - - // Else don't animate, just show or hide - - else { - - if(bShow) { - - oContainer._oContent.style.display = "block"; - - this.containerExpandEvent.fire(this); - - } - - else { - - oContainer._oContent.style.display = "none"; - - this.containerCollapseEvent.fire(this); - - } - - this._toggleContainerHelpers(bShow); - - this._bContainerOpen = bShow; - - } - - - -}; - - - -/** - - * Toggles the highlight on or off for an item in the container, and also cleans - - * up highlighting of any previous item. - - * - - * @param {object} oNewItem New The <li> element item to receive highlight - - * behavior - - * @param {string} sType "mouseover" will toggle highlight on, and "mouseout" - - * will toggle highlight off. - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(oNewItem, sType) { - - var sHighlight = this.highlightClassName; - - if(this._oCurItem) { - - // Remove highlight from old item - - YAHOO.util.Dom.removeClass(this._oCurItem, sHighlight); - - } - - - - if((sType == "to") && sHighlight) { - - // Apply highlight to new item - - YAHOO.util.Dom.addClass(oNewItem, sHighlight); - - this._oCurItem = oNewItem; - - } - -}; - - - -/** - - * Toggles the pre-highlight on or off for an item in the container. - - * - - * @param {object} oNewItem New The <li> element item to receive highlight - - * behavior - - * @param {string} sType "mouseover" will toggle highlight on, and "mouseout" - - * will toggle highlight off. - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(oNewItem, sType) { - - if(oNewItem == this._oCurItem) { - - return; - - } - - - - var sPrehighlight = this.prehighlightClassName; - - if((sType == "mouseover") && sPrehighlight) { - - // Apply prehighlight to new item - - YAHOO.util.Dom.addClass(oNewItem, sPrehighlight); - - } - - else { - - // Remove prehighlight from old item - - YAHOO.util.Dom.removeClass(oNewItem, sPrehighlight); - - } - -}; - - - -/** - - * Updates the text input box value with selected query result. If a delimiter - - * has been defined, then the value gets appended with the delimiter. - - * - - * @param {object} oItem The <li> element item with which to update the value - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._updateValue = function(oItem) { - - var oTextbox = this._oTextbox; - - var sDelimChar = (this.delimChar) ? this.delimChar[0] : null; - - var sSavedQuery = this._sSavedQuery; - - var sResultKey = oItem._sResultKey; - - oTextbox.focus(); - - - - // First clear text field - - oTextbox.value = ""; - - // Grab data to put into text field - - if(sDelimChar) { - - if(sSavedQuery) { - - oTextbox.value = sSavedQuery; - - } - - oTextbox.value += sResultKey + sDelimChar; - - if(sDelimChar != " ") { - - oTextbox.value += " "; - - } - - } - - else { oTextbox.value = sResultKey; } - - - - // scroll to bottom of textarea if necessary - - if(oTextbox.type == "textarea") { - - oTextbox.scrollTop = oTextbox.scrollHeight; - - } - - - - // move cursor to end - - var end = oTextbox.value.length; - - this._selectText(oTextbox,end,end); - - - - this._oCurItem = oItem; - -}; - - - -/** - - * Selects a result item from the container - - * - - * @param {object} oItem The selected <li> element item - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._selectItem = function(oItem) { - - this._bItemSelected = true; - - this._updateValue(oItem); - - this.itemSelectEvent.fire(this, oItem, oItem._oResultData); - - this._clearList(); - -}; - - - -/** - - * For values updated by type-ahead, the right arrow key jumps to the end - - * of the textbox, otherwise the container is closed. - - * - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._jumpSelection = function() { - - if(!this.typeAhead) { - - return; - - } - - else { - - this._clearList(); - - } - -}; - - - -/** - - * Triggered by up and down arrow keys, changes the current highlighted - - * <li> element item. Scrolls container if necessary. - - * - - * @param {number} nKeyCode Code of key pressed - - * @private - - */ - -YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) { - - if(this._bContainerOpen) { - - // Determine current item's id number - - var oCurItem = this._oCurItem; - - var nCurItemIndex = -1; - - - - if (oCurItem) { - - nCurItemIndex = oCurItem._nItemIndex; - - } - - - - var nNewItemIndex = (nKeyCode == 40) ? - - (nCurItemIndex + 1) : (nCurItemIndex - 1); - - - - // Out of bounds - - if (nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) { - - return; - - } - - - - if (oCurItem) { - - // Unhighlight current item - - this._toggleHighlight(oCurItem, "from"); - - this.itemArrowFromEvent.fire(this, oCurItem); - - } - - if (nNewItemIndex == -1) { - - // Go back to query (remove type-ahead string) - - if(this.delimChar && this._sSavedQuery) { - - if (!this._textMatchesOption()) { - - this._oTextbox.value = this._sSavedQuery; - - } - - else { - - this._oTextbox.value = this._sSavedQuery + this._sCurQuery; - - } - - } - - else { - - this._oTextbox.value = this._sCurQuery; - - } - - this._oCurItem = null; - - return; - - } - - if (nNewItemIndex == -2) { - - // Close container - - this._clearList(); - - return; - - } - - - - var oNewItem = this._aListItems[nNewItemIndex]; - - - - // Scroll the container if necessary - - var oContent = this._oContainer._oContent; - - var scrollOn = ((YAHOO.util.Dom.getStyle(oContent,"overflow") == "auto") || - - (YAHOO.util.Dom.getStyle(oContent,"overflowY") == "auto")); - - if(scrollOn && (nNewItemIndex > -1) && - - (nNewItemIndex < this._nDisplayedItems)) { - - // User is keying down - - if(nKeyCode == 40) { - - // Bottom of selected item is below scroll area... - - if((oNewItem.offsetTop+oNewItem.offsetHeight) > (oContent.scrollTop + oContent.offsetHeight)) { - - // Set bottom of scroll area to bottom of selected item - - oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight; - - } - - // Bottom of selected item is above scroll area... - - else if((oNewItem.offsetTop+oNewItem.offsetHeight) < oContent.scrollTop) { - - // Set top of selected item to top of scroll area - - oContent.scrollTop = oNewItem.offsetTop; - - - - } - - } - - // User is keying up - - else { - - // Top of selected item is above scroll area - - if(oNewItem.offsetTop < oContent.scrollTop) { - - // Set top of scroll area to top of selected item - - this._oContainer._oContent.scrollTop = oNewItem.offsetTop; - - } - - // Top of selected item is below scroll area - - else if(oNewItem.offsetTop > (oContent.scrollTop + oContent.offsetHeight)) { - - // Set bottom of selected item to bottom of scroll area - - this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight; - - } - - } - - } - - - - this._toggleHighlight(oNewItem, "to"); - - this.itemArrowToEvent.fire(this, oNewItem); - - if(this.typeAhead) { - - this._updateValue(oNewItem); - - } - - } - -}; - - - -/****************************************************************************/ - -/****************************************************************************/ - -/****************************************************************************/ - - - -/** - - * Class providing encapsulation of a data source. - - * - - * @constructor - - * - - */ - -YAHOO.widget.DataSource = function() { - - /* abstract class */ - -}; - - - - - -/*************************************************************************** - - * Public constants - - ***************************************************************************/ - -/** - - * Error message for null data responses. - - * - - * @type constant - - * @final - - */ - -YAHOO.widget.DataSource.prototype.ERROR_DATANULL = "Response data was null"; - - - -/** - - * Error message for data responses with parsing errors. - - * - - * @type constant - - * @final - - */ - -YAHOO.widget.DataSource.prototype.ERROR_DATAPARSE = "Response data could not be parsed"; - - - - - -/*************************************************************************** - - * Public member variables - - ***************************************************************************/ - -/** - * Max size of the local cache. Set to 0 to turn off caching. Caching is * useful to reduce the number of server connections. Recommended only for data * sources that return comprehensive results for queries or when stale data is - * not an issue. Default: 15. + * not an issue. * - * @type number + * @property maxCacheEntries + * @type Number + + * @default 15 + */ YAHOO.widget.DataSource.prototype.maxCacheEntries = 15; @@ -3690,12 +2145,16 @@ * queryMatchContains is set to false, meaning the cache only returns results - * that "start with" the query string. Default: false. + * that "start with" the query string. * - * @type boolean + * @property queryMatchContains + * @type Boolean + + * @default false + */ YAHOO.widget.DataSource.prototype.queryMatchContains = false; @@ -3704,22 +2163,28 @@ /** - * Data source query subset matching. If caching is on and queryMatchSubset is + * Enables query subset matching. If caching is on and queryMatchSubset is * true, substrings of queries will return matching cached results. For * instance, if the first query is for "abc" susequent queries that start with * "abc", like "abcd", will be queried against the cache, and not the live data - * source. Recommended only for data sources that return comprehensive results + * source. Recommended only for DataSources that return comprehensive results - * for queries with very few characters. Default: false. + * for queries with very few characters. * - * @type boolean + * @property queryMatchSubset + * @type Boolean + + * @default false + + * + */ YAHOO.widget.DataSource.prototype.queryMatchSubset = false; @@ -3728,16 +2193,20 @@ /** - * Data source query case-sensitivity matching. If caching is on and + * Enables query case-sensitivity matching. If caching is on and * queryMatchCase is true, queries will only return results for case-sensitive - * matches. Default: false. + * matches. * - * @type boolean + * @property queryMatchCase + * @type Boolean + + * @default false + */ YAHOO.widget.DataSource.prototype.queryMatchCase = false; @@ -3746,38 +2215,28 @@ -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Public methods +// - ***************************************************************************/ +// Public methods - /** +// - * Public accessor to the unique name of the data source instance. +///////////////////////////////////////////////////////////////////////////// - * - - * @return {string} Unique name of the data source instance - - */ - -YAHOO.widget.DataSource.prototype.getName = function() { - - return this._sName; - -}; - /** - * Public accessor to the unique name of the data source instance. + * Public accessor to the unique name of the DataSource instance. * - * @return {string} Unique name of the data source instance + * @method toString + * @return {String} Unique name of the DataSource instance + */ YAHOO.widget.DataSource.prototype.toString = function() { @@ -3796,25 +2255,25 @@ * - * @param {object} oCallbackFn Callback function defined by oParent object to + * @method getResults - * which to return results + * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results. - * @param {string} sQuery Query string + * @param sQuery {String} Query string. - * @param {object} oParent The object instance that has requested data + * @param oParent {Object} The object instance that has requested data. */ YAHOO.widget.DataSource.prototype.getResults = function(oCallbackFn, sQuery, oParent) { - + // First look in cache var aResults = this._doQueryCache(oCallbackFn,sQuery,oParent); - + // Not in cache, so get results from server @@ -3840,19 +2299,19 @@ * - * @param {object} oCallbackFn Callback function implemented by oParent to + * @method doQuery - * which to return results + * @param oCallbackFn {HTMLFunction} Callback function implemented by oParent to which to return results. - * @param {string} sQuery Query string + * @param sQuery {String} Query string. - * @param {object} oParent The object instance that has requested data + * @param oParent {Object} The object instance that has requested data. */ YAHOO.widget.DataSource.prototype.doQuery = function(oCallbackFn, sQuery, oParent) { - /* override this */ + /* override this */ }; @@ -3862,6 +2321,10 @@ * Flushes cache. + * + + * @method flushCache + */ YAHOO.widget.DataSource.prototype.flushCache = function() { @@ -3884,24 +2347,32 @@ -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Events +// - ***************************************************************************/ +// Public events +// + +///////////////////////////////////////////////////////////////////////////// + + + /** - * Fired when a query is made to the live data source. Subscribers receive the + * Fired when a query is made to the live data source. - * following array:
+ * - * - args[0] The data source instance + * @event queryEvent - * - args[1] The requesting object + * @param oSelf {Object} The DataSource instance. - * - args[2] The query string + * @param oParent {Object} The requesting object. + * @param sQuery {String} The query string. + */ YAHOO.widget.DataSource.prototype.queryEvent = null; @@ -3910,16 +2381,18 @@ /** - * Fired when a query is made to the local cache. Subscribers receive the + * Fired when a query is made to the local cache. - * following array:
+ * - * - args[0] The data source instance + * @event cacheQueryEvent - * - args[1] The requesting object + * @param oSelf {Object} The DataSource instance. - * - args[2] The query string + * @param oParent {Object} The requesting object. + * @param sQuery {String} The query string. + */ YAHOO.widget.DataSource.prototype.cacheQueryEvent = null; @@ -3928,38 +2401,42 @@ /** - * Fired when data is retrieved from the live data source. Subscribers receive + * Fired when data is retrieved from the live data source. - * the following array:
+ * - * - args[0] The data source instance + * @event getResultsEvent - * - args[1] The requesting object + * @param oSelf {Object} The DataSource instance. - * - args[2] The query string + * @param oParent {Object} The requesting object. - * - args[3] Array of result objects + * @param sQuery {String} The query string. + * @param aResults {Object[]} Array of result objects. + */ YAHOO.widget.DataSource.prototype.getResultsEvent = null; - + /** - * Fired when data is retrieved from the local cache. Subscribers receive the + * Fired when data is retrieved from the local cache. - * following array :
+ * - * - args[0] The data source instance + * @event getCachedResultsEvent - * - args[1] The requesting object + * @param oSelf {Object} The DataSource instance. - * - args[2] The query string + * @param oParent {Object} The requesting object. - * - args[3] Array of result objects + * @param sQuery {String} The query string. + * @param aResults {Object[]} Array of result objects. + */ YAHOO.widget.DataSource.prototype.getCachedResultsEvent = null; @@ -3968,18 +2445,20 @@ /** - * Fired when an error is encountered with the live data source. Subscribers + * Fired when an error is encountered with the live data source. - * receive the following array:
+ * - * - args[0] The data source instance + * @event dataErrorEvent - * - args[1] The requesting object + * @param oSelf {Object} The DataSource instance. - * - args[2] The query string + * @param oParent {Object} The requesting object. - * - args[3] Error message string + * @param sQuery {String} The query string. + * @param sMsg {String} Error message string + */ YAHOO.widget.DataSource.prototype.dataErrorEvent = null; @@ -3988,34 +2467,46 @@ /** - * Fired when the local cache is flushed. Subscribers receive the following + * Fired when the local cache is flushed. - * array :
+ * - * - args[0] The data source instance + * @event cacheFlushEvent + * @param oSelf {Object} The DataSource instance + */ YAHOO.widget.DataSource.prototype.cacheFlushEvent = null; -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Private member variables +// - ***************************************************************************/ +// Private member variables +// + +///////////////////////////////////////////////////////////////////////////// + + + /** - * Internal class variable to index multiple data source instances. + * Internal class variable to index multiple DataSource instances. * - * @type number + * @property _nIndex + * @type Number + * @private + * @static + */ YAHOO.widget.DataSource._nIndex = 0; @@ -4024,12 +2515,14 @@ /** - * Name of data source instance. + * Name of DataSource instance. * - * @type string + * @property _sName + * @type String + * @private */ @@ -4044,8 +2537,10 @@ * - * @type array + * @property _aCache + * @type Object[] + * @private */ @@ -4056,18 +2551,26 @@ -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Private methods +// - ***************************************************************************/ +// Private methods +// + +///////////////////////////////////////////////////////////////////////////// + + + /** - * Initializes data source instance. + * Initializes DataSource instance. - * + * + * @method _init + * @private */ @@ -4092,13 +2595,13 @@ } - + this._sName = "instance" + YAHOO.widget.DataSource._nIndex; YAHOO.widget.DataSource._nIndex++; - + this.queryEvent = new YAHOO.util.CustomEvent("query", this); @@ -4118,35 +2621,35 @@ /** - * Adds a result object to the local cache, evicting the oldest element if the + * Adds a result object to the local cache, evicting the oldest element if the * cache is full. Newer items will have higher indexes, the oldest item will have - * index of 0. + * index of 0. * - * @param {object} resultObj Object literal of data results, including internal + * @method _addCacheElem - * properties and an array of result objects + * @param oResult {Object} Data result object, including array of results. * @private */ -YAHOO.widget.DataSource.prototype._addCacheElem = function(resultObj) { +YAHOO.widget.DataSource.prototype._addCacheElem = function(oResult) { var aCache = this._aCache; // Don't add if anything important is missing. - if(!aCache || !resultObj || !resultObj.query || !resultObj.results) { + if(!aCache || !oResult || !oResult.query || !oResult.results) { return; } - + // If the cache is full, make room by removing from index=0 @@ -4156,11 +2659,11 @@ } - + // Add to cache, at the end of the array - aCache.push(resultObj); + aCache.push(oResult); }; @@ -4172,24 +2675,22 @@ * function is called with the results, and the cached is refreshed so that it - * is now the newest element. + * is now the newest element. * - * @param {object} oCallbackFn Callback function defined by oParent object to + * @method _doQueryCache - * which to return results + * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results. - * @param {string} sQuery Query string + * @param sQuery {String} Query string. - * @param {object} oParent The object instance that has requested data + * @param oParent {Object} The object instance that has requested data. - * @return {array} aResults Result object from local cache if found, otherwise + * @return aResults {Object[]} Array of results from local cache if found, otherwise null. - * null + * @private - * @private - */ YAHOO.widget.DataSource.prototype._doQueryCache = function(oCallbackFn, sQuery, oParent) { @@ -4204,7 +2705,7 @@ var bMatchContains = this.queryMatchContains; - + // If cache is enabled... @@ -4236,11 +2737,11 @@ var matchKey = (!this.queryMatchCase) ? - encodeURIComponent(resultObj.query.toLowerCase()): + encodeURIComponent(resultObj.query).toLowerCase(): encodeURIComponent(resultObj.query); - + // If a cached match key exactly matches the query... @@ -4252,13 +2753,13 @@ aResults = aAllResultItems; - + // The matching cache element was not the most recent, // so now we need to refresh the cache. - if(i != nCacheLength-1) { + if(i != nCacheLength-1) { // Remove element from its original location @@ -4284,15 +2785,15 @@ var subQuery = sQuery.substr(0,j); - + // If a substring of a cached sQuery exactly matches the query... - if(matchKey == subQuery) { + if(matchKey == subQuery) { bMatchFound = true; - + // Go through each cached result object to match against the query... @@ -4306,7 +2807,7 @@ encodeURIComponent(aRecord[0]).toLowerCase().indexOf(sQuery); - + // A STARTSWITH match is when the query is found at the beginning of the key string... @@ -4324,7 +2825,7 @@ } - + // Add the subset match result set object as the newest element to cache, @@ -4354,7 +2855,7 @@ } - + // If there was a match, send along the results. @@ -4390,21 +2891,23 @@ * query results. - * requires YAHOO.util.Connect XMLHTTPRequest library + * - * extends YAHOO.widget.DataSource + * @class DS_XHR - * + * @extends YAHOO.widget.DataSource + * @requires connection + * @constructor - * @param {string} sScriptURI Absolute or relative URI to script that returns + * @param sScriptURI {String} Absolute or relative URI to script that returns query - * query results as JSON, XML, or delimited flat data + * results as JSON, XML, or delimited flat-file data. - * @param {array} aSchema Data schema definition of results + * @param aSchema {String[]} Data schema definition of results. - * @param {object} oConfigs Optional object literal of config params + * @param oConfigs {Object} (optional) Object literal of config params. */ @@ -4422,7 +2925,7 @@ } - + // Initialization sequence @@ -4450,57 +2953,75 @@ -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Public constants +// - ***************************************************************************/ +// Public constants +// + +///////////////////////////////////////////////////////////////////////////// + + + /** - * JSON data type + * JSON data type. * - * @type constant + * @property TYPE_JSON + * @type Number + + * @static + * @final */ -YAHOO.widget.DS_XHR.prototype.TYPE_JSON = 0; +YAHOO.widget.DS_XHR.TYPE_JSON = 0; /** - * XML data type + * XML data type. * - * @type constant + * @property TYPE_XML + * @type Number + + * @static + * @final */ -YAHOO.widget.DS_XHR.prototype.TYPE_XML = 1; +YAHOO.widget.DS_XHR.TYPE_XML = 1; /** - * Flat file data type + * Flat-file data type. * - * @type constant + * @property TYPE_FLAT + * @type Number + + * @static + * @final */ -YAHOO.widget.DS_XHR.prototype.TYPE_FLAT = 2; +YAHOO.widget.DS_XHR.TYPE_FLAT = 2; @@ -4510,56 +3031,86 @@ * - * @type constant + * @property ERROR_DATAXHR + * @type String + + * @static + * @final */ -YAHOO.widget.DS_XHR.prototype.ERROR_DATAXHR = "XHR response failed"; +YAHOO.widget.DS_XHR.ERROR_DATAXHR = "XHR response failed"; -/*************************************************************************** +///////////////////////////////////////////////////////////////////////////// - * Public member variables +// - ***************************************************************************/ +// Public member variables +// + +///////////////////////////////////////////////////////////////////////////// + + + /** + * Alias to YUI Connection Manager. Allows implementers to specify their own + + * subclasses of the YUI Connection Manager utility. + + * + + * @property connMgr + + * @type Object + + * @default YAHOO.util.Connect + + */ + +YAHOO.widget.DS_XHR.prototype.connMgr = YAHOO.util.Connect; + + + +/** + * Number of milliseconds the XHR connection will wait for a server response. A * a value of zero indicates the XHR connection will wait forever. Any value * greater than zero will use the Connection utility's Auto-Abort feature. - * Default: 0. - * - * @type number + * @property connTimeout + * @type Number + + * @default 0 + */ YAHOO.widget.DS_XHR.prototype.connTimeout = 0; - - /** * Absolute or relative URI to script that returns query results. For instance, - * queries will be sent to + * queries will be sent to <scriptURI>?<scriptQueryParam>=userinput - * ?=userinput - * - * @type string + * @property scriptURI + * @type String + */ YAHOO.widget.DS_XHR.prototype.scriptURI = null; @@ -4570,15 +3121,15 @@ * Query string parameter name sent to scriptURI. For instance, queries will be - * sent to + * sent to <scriptURI>?<scriptQueryParam>=userinput - * ?=userinput + * - * Default: "query". + * @property scriptQueryParam - * + * @type String - * @type string + * @default "query" */ @@ -4594,14 +3145,16 @@ * When defined, queries will be sent to - * ?=userinput& + * <scriptURI>?<scriptQueryParam>=userinput&<scriptQueryAppend> - * Default: "". - * - * @type string + * @property scriptQueryAppend + * @type String + + * @default "" + */ YAHOO.widget.DS_XHR.prototype.scriptQueryAppend = ""; @@ -4610,17 +3163,21 @@ /** - * XHR response data type. Other types that may be defined are TYPE_XML and + * XHR response data type. Other types that may be defined are YAHOO.widget.DS_XHR.TYPE_XML - * TYPE_FLAT. Default: TYPE_JSON. + * and YAHOO.widget.DS_XHR.TYPE_FLAT. * - * @type type + * @property responseType + * @type String + + * @default YAHOO.widget.DS_XHR.TYPE_JSON + */ -YAHOO.widget.DS_XHR.prototype.responseType = YAHOO.widget.DS_XHR.prototype.TYPE_JSON; +YAHOO.widget.DS_XHR.prototype.responseType = YAHOO.widget.DS_XHR.TYPE_JSON; @@ -4630,45 +3187,55 @@ * back as HTML, the gzip HTML comment appears at the end of the data and should - * be ignored. Default: "\n<!--" + * be ignored. * - * @type string + * @property responseStripAfter + * @type String + + * @default "\n<!-" + */ -YAHOO.widget.DS_XHR.prototype.responseStripAfter = "\n