autocomplete.js 95 KB

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