autocomplete-debug.js 99 KB

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