Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

aui-datasource-control.js 25KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. AUI.add('aui-datasource-control-base', function(A) {
  2. var Lang = A.Lang,
  3. isFunction = Lang.isFunction,
  4. isString = Lang.isString,
  5. BINDUI = 'bindUI',
  6. RENDERUI = 'renderUI',
  7. SYNCUI = 'syncUI';
  8. var DataSourceControl = function() {};
  9. DataSourceControl.ATTRS = {
  10. /**
  11. * The data source that results will be read from. This can either be
  12. * an existing <a href="DataSource.html">DataSource</a> object, or it can be a
  13. * value that would be passed to <a href="DataSource.html">DataSource</a>.
  14. *
  15. * @attribute dataSource
  16. * @default null
  17. * @type Object | String | Function | Array
  18. */
  19. dataSource: {
  20. value: null,
  21. setter: function(val) {
  22. var instance = this;
  23. var dataSource = val;
  24. if (val) {
  25. var data = dataSource;
  26. var dataSourceType = instance.get('dataSourceType');
  27. if (!(dataSource instanceof A.DataSource.Local)) {
  28. if (!dataSourceType) {
  29. dataSourceType = 'Local';
  30. if (isFunction(data)) {
  31. dataSourceType = 'Function';
  32. }
  33. else if (isString(data)) {
  34. dataSourceType = 'IO';
  35. }
  36. }
  37. dataSource = new A.DataSource[dataSourceType](
  38. {
  39. source: data
  40. }
  41. );
  42. }
  43. dataSourceType = dataSource.name;
  44. var schema = instance._schema;
  45. if (schema) {
  46. dataSource.plug(schema);
  47. }
  48. instance.set('dataSourceType', dataSourceType);
  49. }
  50. return dataSource;
  51. }
  52. },
  53. /**
  54. * The type of the data source passed into <a href="AutoComplete.html#config_dataSource">dataSource</a>.
  55. * This can be used to explicitly declare what kind of <a href="DataSource.html">DataSource</a> object will
  56. * be created.
  57. *
  58. * @attribute dataSourceType
  59. * @default null
  60. * @type String
  61. */
  62. dataSourceType: {
  63. value: null
  64. },
  65. /**
  66. * A valid configuration object for any of <a href="module_datasource.html">DataSource</a> schema plugins.
  67. *
  68. * @attribute schema
  69. * @default null
  70. * @type Object
  71. */
  72. schema: {
  73. value: null,
  74. lazyAdd: false,
  75. setter: function(val) {
  76. var instance = this;
  77. var dataSource = instance.get('dataSource');
  78. var schema = instance._schema;
  79. if (dataSource && schema) {
  80. dataSource.unplug(schema);
  81. schema = null;
  82. instance._schema = null;
  83. }
  84. if (val) {
  85. if (val.fn) {
  86. schema = val;
  87. val = val.cfg.schema;
  88. }
  89. else {
  90. var schemaType = instance.get('schemaType');
  91. var schemaTypes = {
  92. array: A.Plugin.DataSourceArraySchema,
  93. json: A.Plugin.DataSourceJSONSchema,
  94. text: A.Plugin.DataSourceTextSchema,
  95. xml: A.Plugin.DataSourceXMLSchema
  96. };
  97. schemaType = schemaType.toLowerCase() || 'array';
  98. schema = {
  99. fn: schemaTypes[schemaType],
  100. cfg: {
  101. schema: val
  102. }
  103. };
  104. }
  105. }
  106. if (dataSource && schema) {
  107. dataSource.plug(schema);
  108. }
  109. instance._schema = schema;
  110. return val;
  111. }
  112. },
  113. /**
  114. * A valid type of <a href="module_datasource.html">DataSource</a> schema plugin, such as array, json, xml, etc.
  115. *
  116. * @attribute schemaType
  117. * @default array
  118. * @type String
  119. */
  120. schemaType: {
  121. value: '',
  122. lazyAdd: false,
  123. validator: isString
  124. }
  125. };
  126. DataSourceControl.prototype = {
  127. initializer: function() {
  128. var instance = this;
  129. instance.publish(RENDERUI);
  130. instance.publish(BINDUI);
  131. instance.publish(SYNCUI);
  132. },
  133. renderUI: function() {
  134. var instance = this;
  135. instance.fire(RENDERUI);
  136. },
  137. bindUI: function() {
  138. var instance = this;
  139. /**
  140. * Handles the dataError event. Fired when there is an error accessing the data.
  141. *
  142. * @event dataError
  143. * @param {Event.Facade} event The dataError event.
  144. */
  145. instance.publish('dataError');
  146. /**
  147. * Handles the dataRequest event. Fired when ever a query is sent to the data source.
  148. *
  149. * @event dataRequest
  150. * @param {Event.Facade} event The dataRequest event.
  151. */
  152. instance.publish('dataRequest');
  153. /**
  154. * Handles the dataReturn event. Fired when data successfully comes back from the data request.
  155. *
  156. * @event dataReturn
  157. * @param {Event.Facade} event The dataReturn event.
  158. */
  159. instance.publish('dataReturn');
  160. instance.fire(BINDUI);
  161. },
  162. syncUI: function() {
  163. var instance = this;
  164. instance.fire(SYNCUI);
  165. }
  166. }
  167. A.DataSourceControl = DataSourceControl;
  168. }, '@VERSION@' ,{requires:['aui-base','datasource','dataschema']});
  169. AUI.add('aui-input-text-control', function(A) {
  170. var Lang = A.Lang,
  171. isArray = Lang.isArray,
  172. isFunction = Lang.isFunction,
  173. isString = Lang.isString,
  174. KeyMap = A.Event.KeyMap,
  175. ALERT = 'alert',
  176. BINDUI = 'bindUI',
  177. CONTENT = 'content',
  178. ICON = 'icon',
  179. LIST = 'list',
  180. LOADING = 'loading',
  181. RENDERUI = 'renderUI',
  182. ICON_DEFAULT = 'circle-triangle-b',
  183. ICON_ERROR = ALERT,
  184. ICON_LOADING = LOADING,
  185. BACKSPACE = 'BACKSPACE',
  186. TAB = 'TAB',
  187. ALT = 'ALT',
  188. WIN_IME = 'WIN_IME',
  189. OVERLAY_ALIGN = {
  190. node: null,
  191. points: ['tl', 'bl']
  192. },
  193. BOUNDING_BOX = 'boundingBox',
  194. CONTENT_BOX = 'contentBox';
  195. var InputTextControl = function() {
  196. var instance = this;
  197. instance.on(RENDERUI, instance._renderUIInputTextControl, instance);
  198. instance.on(BINDUI, instance._bindUIInputTextControl, instance);
  199. };
  200. /**
  201. * Static property used to define the default attribute
  202. * configuration for the AutoComplete.
  203. *
  204. * @property AutoComplete.ATTRS
  205. * @type Object
  206. * @static
  207. */
  208. InputTextControl.ATTRS = {
  209. /**
  210. * To use a button
  211. *
  212. * @attribute button
  213. * @default true
  214. * @type Boolean
  215. * @deprecated
  216. */
  217. button: {
  218. value: true
  219. },
  220. /**
  221. * The character used to indicate the beginning or ending of a new value. Most commonly used
  222. * is a ",".
  223. *
  224. * @attribute delimChar
  225. * @default null
  226. * @type String
  227. */
  228. delimChar: {
  229. value: null,
  230. setter: function(value) {
  231. if (isString(value) && (value.length > 0)) {
  232. value = [value];
  233. }
  234. else if (!isArray(value)) {
  235. value = A.Attribute.INVALID_VALUE;
  236. }
  237. return value;
  238. }
  239. },
  240. /**
  241. * If <a href="AutoComplete.html#config_typeAhead">typeAhead</a> is true, this
  242. * will clear a selection when the overlay closes unless a user explicitly selects an item.
  243. *
  244. * @attribute forceSelection
  245. * @default false
  246. * @type Boolean
  247. */
  248. forceSelection: {
  249. value: false
  250. },
  251. iconButton: {
  252. value: ICON_DEFAULT
  253. },
  254. /**
  255. * The input field which will recieve the users input.
  256. *
  257. * @attribute input
  258. * @default null
  259. * @type String | Node
  260. */
  261. input: {
  262. value: null
  263. },
  264. /**
  265. * The key or numeric index in the schema to match the result against.
  266. *
  267. * @attribute matchKey
  268. * @default 0
  269. * @type String | Number
  270. */
  271. matchKey: {
  272. value: 0
  273. },
  274. /**
  275. * The minimum number of characters required to query the data source.
  276. *
  277. * @attribute minQueryLength
  278. * @default 1
  279. * @type Number
  280. */
  281. minQueryLength: {
  282. value: 1
  283. },
  284. /**
  285. * The amount of time in seconds to delay before submitting the query.
  286. *
  287. * @attribute queryDelay
  288. * @default 0.2
  289. * @type Number
  290. */
  291. queryDelay: {
  292. value: 0.2,
  293. getter: function(value) {
  294. return value * 1000;
  295. }
  296. },
  297. /**
  298. * When IME usage is detected or interval detection is explicitly enabled,
  299. * AutoComplete will detect the input value at the given interval and send a
  300. * query if the value has changed.
  301. *
  302. * @attribute queryInterval
  303. * @default 0.5
  304. * @type Number
  305. */
  306. queryInterval: {
  307. value: 0.5,
  308. getter: function(value) {
  309. return value * 1000;
  310. }
  311. },
  312. /**
  313. * When <a href="AutoComplete.html#config_applyLocalFilter">applyLocalFilter</a> is true,
  314. * setting this to true will match only results with the same case.
  315. *
  316. * @attribute queryMatchCase
  317. * @default false
  318. * @type Boolean
  319. */
  320. queryMatchCase: {
  321. value: false
  322. },
  323. /**
  324. * When <a href="AutoComplete.html#config_applyLocalFilter">applyLocalFilter</a> is true,
  325. * setting this to true will match results which contain the query anywhere in the text,
  326. * instead of just matching just items that start with the query.
  327. *
  328. * @attribute queryMatchContains
  329. * @default false
  330. * @type Boolean
  331. */
  332. queryMatchContains: {
  333. value: false
  334. },
  335. /**
  336. * For IO DataSources, AutoComplete will automatically insert a "?" between the server URI and
  337. * the encoded query string. To prevent this behavior, you can
  338. * set this value to false. If you need to customize this even further, you
  339. * can override the <a href="AutoComplete.html#method_generateRequest">generateRequest</a> method.
  340. *
  341. * @attribute queryQuestionMark
  342. * @default true
  343. * @type Boolean
  344. */
  345. queryQuestionMark: {
  346. value: true
  347. },
  348. /**
  349. * Whether or not the input field should be updated with selections.
  350. *
  351. * @attribute suppressInputUpdate
  352. * @default false
  353. * @type Boolean
  354. */
  355. suppressInputUpdate: {
  356. value: false
  357. },
  358. /**
  359. * If <a href="AutoComplete.html#config_autoHighlight">autoHighlight</a> is enabled, whether or not the
  360. * input field should be automatically updated with the first result as the user types,
  361. * automatically selecting the portion of the text the user has not typed yet.
  362. *
  363. * @attribute typeAhead
  364. * @default false
  365. * @type Boolean
  366. */
  367. typeAhead: {
  368. value: false
  369. },
  370. /**
  371. * If <a href="AutoComplete.html#config_typeAhead">typeAhead</a> is true, number of seconds
  372. * to delay before updating the input. In order to prevent certain race conditions, this value must
  373. * always be greater than the <a href="AutoComplete.html#config_queryDelay">queryDelay</a>.
  374. *
  375. * @attribute typeAheadDelay
  376. * @default 0.2
  377. * @type Number
  378. */
  379. typeAheadDelay: {
  380. value: 0.2,
  381. getter: function(value) {
  382. return value * 1000;
  383. }
  384. },
  385. /**
  386. * The unique ID of the input element.
  387. *
  388. * @attribute uniqueName
  389. * @default null
  390. * @type String
  391. */
  392. uniqueName: {
  393. value: null
  394. }
  395. };
  396. InputTextControl.prototype = {
  397. /**
  398. * Construction logic executed during AutoComplete instantiation. Lifecycle.
  399. *
  400. * @method initializer
  401. * @protected
  402. */
  403. initializer: function(config) {
  404. var instance = this;
  405. instance._overlayAlign = A.mix({}, OVERLAY_ALIGN);
  406. },
  407. /**
  408. * Create the DOM structure for the InputTextControl. Lifecycle.
  409. *
  410. * @method _renderUIInputTextControl
  411. * @protected
  412. */
  413. _renderUIInputTextControl: function() {
  414. var instance = this;
  415. instance._renderInput();
  416. },
  417. /**
  418. * Bind the events on the InputTextControl UI. Lifecycle.
  419. *
  420. * @method _bindUIInputTextControl
  421. * @protected
  422. */
  423. _bindUIInputTextControl: function() {
  424. var instance = this;
  425. instance._bindDataSource();
  426. var button = instance.button;
  427. var inputNode = instance.inputNode;
  428. inputNode.on('blur', instance._onTextboxBlur, instance);
  429. inputNode.on('focus', instance._onTextboxFocus, instance);
  430. inputNode.on('keydown', instance._onTextboxKeyDown, instance);
  431. inputNode.on('keypress', instance._onTextboxKeyPress, instance);
  432. inputNode.on('keyup', instance._onTextboxKeyUp, instance);
  433. instance.publish('handleResponse');
  434. instance.publish('textboxKeyDown');
  435. instance.publish('textboxKeyPress');
  436. instance.publish('textboxKeyUp');
  437. instance.publish('invalidQueryLength');
  438. instance.publish('sendQueryDisabled');
  439. /**
  440. * Handles the containerCollapse event. Fired when the container is hidden.
  441. *
  442. * @event containerCollapse
  443. * @param {Event.Facade} event The containerCollapse event.
  444. */
  445. instance.publish('containerCollapse');
  446. /**
  447. * Handles the containerExpand event. Fired when the container is shown.
  448. *
  449. * @event containerExpand
  450. * @param {Event.Facade} event The containerExpand event.
  451. */
  452. instance.publish('containerExpand');
  453. /**
  454. * Handles the containerPopulate event. Fired when the container is populated.
  455. *
  456. * @event containerPopulate
  457. * @param {Event.Facade} event The containerPopulate event.
  458. */
  459. instance.publish('containerPopulate');
  460. /**
  461. * Handles the itemArrowFrom event. Fired when the user navigates via the keyboard away from
  462. * a selected item.
  463. *
  464. * @event itemArrowFrom
  465. * @param {Event.Facade} event The itemArrowFrom event.
  466. */
  467. instance.publish('itemArrowFrom');
  468. /**
  469. * Handles the itemArrowTo event. Fired when the user navigates via the keyboard to a selected item.
  470. *
  471. * @event itemArrowTo
  472. * @param {Event.Facade} event The itemArrowTo event.
  473. */
  474. instance.publish('itemArrowTo');
  475. /**
  476. * Handles the itemMouseOut event. Fired when the user mouses away from an item.
  477. *
  478. * @event itemMouseOut
  479. * @param {Event.Facade} event The itemMouseOut event.
  480. */
  481. instance.publish('itemMouseOut');
  482. /**
  483. * Handles the itemMouseOver event. Fired when the user mouses over an item.
  484. *
  485. * @event itemMouseOver
  486. * @param {Event.Facade} event The itemMouseOver event.
  487. */
  488. instance.publish('itemMouseOver');
  489. /**
  490. * Handles the itemSelect event. Fired when an item in the list is selected.
  491. *
  492. * @event itemSelect
  493. * @param {Event.Facade} event The itemSelect event.
  494. */
  495. instance.publish('itemSelect');
  496. /**
  497. * Handles the selectionEnforce event. Fired if <a href="Autocomplete.html#config_forceSelection">forceSelection</a>
  498. * is enabled and the users input element has been cleared because it did not match one of the results.
  499. *
  500. * @event selectionEnforce
  501. * @param {Event.Facade} event The selectionEnforce event.
  502. */
  503. instance.publish('selectionEnforce');
  504. /**
  505. * Handles the textboxBlur event. Fired when the user leaves the input element.
  506. *
  507. * @event textboxBlur
  508. * @param {Event.Facade} event The textboxBlur event.
  509. */
  510. instance.publish('textboxBlur');
  511. /**
  512. * Handles the textboxChange event. Fired when the value in the input element is changed.
  513. *
  514. * @event textboxChange
  515. * @param {Event.Facade} event The textboxChange event.
  516. */
  517. instance.publish('textboxChange');
  518. /**
  519. * Handles the textboxFocus event. Fired when user moves focus to the input element.
  520. *
  521. * @event textboxFocus
  522. * @param {Event.Facade} event The textboxFocus event.
  523. */
  524. instance.publish('textboxFocus');
  525. /**
  526. * Handles the textboxKey event. Fired when the input element receives key input.
  527. *
  528. * @event textboxKey
  529. * @param {Event.Facade} event The textboxKey event.
  530. */
  531. instance.publish('textboxKey');
  532. /**
  533. * Handles the typeAhead event. Fired when the input element has been pre-filled by the type-ahead feature.
  534. *
  535. * @event typeAhead
  536. * @param {Event.Facade} event The typeAhead event.
  537. */
  538. instance.publish('typeAhead');
  539. /**
  540. * Handles the unmatchedItemSelect event. Fired when a user selects something that does
  541. * not match any of the displayed results.
  542. *
  543. * @event unmatchedItemSelect
  544. * @param {Event.Facade} event The unmatchedItemSelect event.
  545. */
  546. instance.publish('unmatchedItemSelect');
  547. },
  548. /**
  549. * Sync the AutoComplete UI. Lifecycle.
  550. *
  551. * @method syncUI
  552. * @protected
  553. */
  554. syncUI: function() {
  555. var instance = this;
  556. instance.inputNode.setAttribute('autocomplete', 'off');
  557. },
  558. /**
  559. * An overridable method that is executed before the result overlay is loaded with results.
  560. *
  561. * @method doBeforeLoadData
  562. * @param {EventFacade} event
  563. * @return {Boolean}
  564. */
  565. doBeforeLoadData: function(event) {
  566. return true;
  567. },
  568. /**
  569. * An overridable method for formatting the result of the query before it's displayed in the overlay.
  570. *
  571. * @method formatResult
  572. * @param {Object} result The result data object
  573. * @param {String} request The current query string
  574. * @param {String} resultMatch The string from the results that matches the query
  575. * @return {String}
  576. */
  577. formatResult: function(result, request, resultMatch) {
  578. return resultMatch || '';
  579. },
  580. /**
  581. * An overridable method that creates an object to be passed to the sendRequest
  582. * method of the data source object. Useful to overwrite if you wish to create
  583. * a custom request object before it's sent.
  584. *
  585. * @method generateRequest
  586. * @param {String} query The string currently being entered
  587. * @return {Object}
  588. */
  589. generateRequest: function(query) {
  590. return {
  591. request: query
  592. };
  593. },
  594. /**
  595. * Handles the response for the display of the results. This is a callback method
  596. * that is fired by the sendRequest method so that results are ready to be accessed.
  597. *
  598. * @method handleResponse
  599. * @param {EventFacade} event
  600. */
  601. handleResponse: function(event) {
  602. var instance = this;
  603. instance.fire('handleResponse', event);
  604. var iconClass = instance.get('iconButton') || ICON_DEFAULT;
  605. if (event.error) {
  606. iconClass = ICON_ERROR;
  607. }
  608. instance.button.set(ICON, iconClass);
  609. },
  610. _bindDataSource: function() {
  611. var instance = this;
  612. var button = instance.button;
  613. var dataSource = instance.get('dataSource');
  614. var dataSourceType = instance.get('dataSourceType');
  615. dataSource.on('request', A.bind(button.set, button, ICON, ICON_LOADING));
  616. dataSource.on('error', instance.handleResponse, instance);
  617. dataSource.after('response', instance.handleResponse, instance);
  618. },
  619. /**
  620. * Clears the query interval
  621. *
  622. * @method _clearInterval
  623. * @private
  624. */
  625. _clearInterval: function() {
  626. var instance = this;
  627. if (instance._queryIntervalId) {
  628. clearInterval(instance._queryIntervalId);
  629. instance._queryIntervalId = null;
  630. }
  631. },
  632. /**
  633. * When <a href="Autocomplete.html#config_forceSelection">forceSelection</a> is true and
  634. * the user tries to leave the input element without selecting an item from the results,
  635. * the user selection is cleared.
  636. *
  637. * @method _clearSelection
  638. * @protected
  639. */
  640. _clearSelection: function() {
  641. var instance = this;
  642. var delimChar = instance.get('delimChar');
  643. var extraction = {
  644. previous: '',
  645. query: instance.inputNode.get('value')
  646. };
  647. if (delimChar) {
  648. extraction = instance._extractQuery(instance.inputNode.get('value'));
  649. }
  650. instance.fire('selectionEnforce', extraction.query);
  651. },
  652. /**
  653. * Enables query interval detection for IME support.
  654. *
  655. * @method _enableIntervalDetection
  656. * @protected
  657. */
  658. _enableIntervalDetection: function() {
  659. var instance = this;
  660. var queryInterval = instance.get('queryInterval');
  661. if (!instance._queryIntervalId && queryInterval) {
  662. instance._queryInterval = setInterval(A.bind(instance._onInterval, instance), queryInterval);
  663. }
  664. },
  665. /**
  666. * Extracts the right most query from the delimited string in the input.
  667. *
  668. * @method _extractQuery
  669. * @param {String} query String to parse
  670. * @protected
  671. * @return {String}
  672. */
  673. _extractQuery: function(query) {
  674. var instance = this;
  675. var delimChar = instance.get('delimChar');
  676. var delimIndex = -1;
  677. var i = delimChar.length - 1;
  678. var newIndex, queryStart, previous;
  679. for (; i >= 0; i--) {
  680. newIndex = query.lastIndexOf(delimChar[i]);
  681. if (newIndex > delimIndex) {
  682. delimIndex = newIndex;
  683. }
  684. }
  685. if (delimChar[i] == ' ') {
  686. for (var j = delimChar.length - 1; j >= 0; j--){
  687. if (query[delimIndex - 1] == delimChar[j]) {
  688. delimIndex--;
  689. break;
  690. }
  691. }
  692. }
  693. if (delimIndex > -1) {
  694. queryStart = delimIndex + 1;
  695. while (query.charAt(queryStart) == ' ') {
  696. queryStart += 1;
  697. }
  698. previous = query.substring(0, queryStart);
  699. query = query.substring(queryStart);
  700. }
  701. else {
  702. previous = '';
  703. }
  704. return {
  705. previous: previous,
  706. query: query
  707. };
  708. },
  709. /**
  710. * Focuses the input element.
  711. *
  712. * @method _focus
  713. * @protected
  714. */
  715. _focus: function() {
  716. var instance = this;
  717. setTimeout(
  718. function() {
  719. instance.inputNode.focus();
  720. },
  721. 1
  722. );
  723. },
  724. /**
  725. * Called when the user mouses down on the button element in the combobox.
  726. *
  727. * @method _onButtonMouseDown
  728. * @param {EventFacade} event
  729. * @protected
  730. */
  731. _onButtonMouseDown: function(event) {
  732. var instance = this;
  733. event.halt();
  734. instance._focus();
  735. instance._sendQuery(instance.inputNode.get('value') + '*');
  736. },
  737. /**
  738. * Enables the query to be triggered based on detecting text input via intervals instead of via
  739. * key events.
  740. *
  741. * @method _onInterval
  742. * @protected
  743. */
  744. _onInterval: function() {
  745. var instance = this;
  746. var curValue = instance.inputNode.get('value');
  747. var lastValue = instance._lastValue;
  748. if (curValue != lastValue) {
  749. instance._lastValue = curValue;
  750. instance._sendQuery(curValue);
  751. }
  752. },
  753. /**
  754. * Handles the input element losing focus.
  755. *
  756. * @method _onTextboxBlur
  757. * @param {EventFacade} event
  758. * @protected
  759. */
  760. _onTextboxBlur: function(event) {
  761. var instance = this;
  762. if (!instance._overContainer || KeyMap.isKey(instance._keyCode, TAB)) {
  763. instance.fire('textboxBlur');
  764. }
  765. else {
  766. instance._focus();
  767. }
  768. },
  769. /**
  770. * Handles the input element gaining focus.
  771. *
  772. * @method _onTextboxFocus
  773. * @param {EventFacade} event
  774. * @protected
  775. */
  776. _onTextboxFocus: function(event) {
  777. var instance = this;
  778. if (!instance.get('focused')) {
  779. instance.inputNode.setAttribute('autocomplete', 'off');
  780. instance.focus();
  781. instance._initInputValue = instance.inputNode.get('value');
  782. instance.fire('textboxFocus');
  783. }
  784. },
  785. /**
  786. * Handles the keydown events on the input element for functional keys.
  787. *
  788. * @method _onTextboxKeyDown
  789. * @param {EventFacade} event
  790. * @protected
  791. */
  792. _onTextboxKeyDown: function(event) {
  793. var instance = this;
  794. var keyCode = event.keyCode;
  795. if (instance._typeAheadDelayId != -1) {
  796. clearTimeout(instance._typeAheadDelayId);
  797. }
  798. instance.fire('textboxKeyDown', event);
  799. if (event.isKey(ALT)) {
  800. instance._enableIntervalDetection();
  801. }
  802. instance._keyCode = keyCode;
  803. },
  804. /**
  805. * Handles the key press events of the input element.
  806. *
  807. * @method _onTextboxKeyPress
  808. * @param {EventFacade} event
  809. * @protected
  810. */
  811. _onTextboxKeyPress: function(event) {
  812. var instance = this;
  813. instance.fire('textboxKeyPress', event);
  814. if (event.isKey(WIN_IME)) {
  815. instance._enableIntervalDetection();
  816. }
  817. },
  818. /**
  819. * Handles the keyup events of the input element.
  820. *
  821. * @method _onTextboxKeyUp
  822. * @param {EventFacade} event
  823. * @protected
  824. */
  825. _onTextboxKeyUp: function(event) {
  826. var instance = this;
  827. if (event.isSpecialKey() && !event.isKey(BACKSPACE)) {
  828. return;
  829. }
  830. instance.fire('textboxKeyUp', event);
  831. },
  832. /**
  833. * Handles the rendering of the input element.
  834. *
  835. * @method _renderInput
  836. * @protected
  837. */
  838. _renderInput: function() {
  839. var instance = this;
  840. var contentBox = instance.get(CONTENT_BOX);
  841. var input = instance.get('input');
  842. var iconButton = instance.get('iconButton') || ICON_DEFAULT;
  843. var comboConfig = {
  844. field: {
  845. labelText: false
  846. },
  847. icons: [
  848. {
  849. icon: iconButton,
  850. id: 'trigger',
  851. handler: {
  852. fn: instance._onButtonMouseDown,
  853. context: instance
  854. }
  855. }
  856. ]
  857. };
  858. var inputReference = null;
  859. var inputParent = null;
  860. if (input) {
  861. input = A.one(input);
  862. comboConfig.field.node = input;
  863. inputReference = input.next();
  864. inputParent = input.get('parentNode');
  865. }
  866. var comboBox = new A.Combobox(comboConfig).render(contentBox);
  867. if (inputParent) {
  868. var comboBoundingBox = comboBox.get('boundingBox');
  869. inputParent.insertBefore(comboBoundingBox, inputReference);
  870. }
  871. instance.inputNode = comboBox.get('node');
  872. instance.button = comboBox.icons.item('trigger');
  873. instance.comboBox = comboBox;
  874. instance.set('uniqueName', A.stamp(instance.inputNode));
  875. },
  876. /**
  877. * Makes a query request to the data source.
  878. *
  879. * @method _sendQuery
  880. * @param {String} query The query string
  881. * @protected
  882. */
  883. _sendQuery: function(query) {
  884. var instance = this;
  885. if (instance.get('disabled')) {
  886. instance.fire('sendQueryDisabled', query);
  887. return;
  888. }
  889. var delimChar = instance.get('delimChar');
  890. var minQueryLength = instance.get('minQueryLength');
  891. if (delimChar) {
  892. var extraction = instance._extractQuery(query);
  893. query = extraction.query;
  894. instance._pastSelections = extraction.previous;
  895. }
  896. if ((query && (query.length < minQueryLength)) || (!query && minQueryLength > 0)) {
  897. instance.fire('invalidQueryLength', query);
  898. return;
  899. }
  900. query = encodeURIComponent(query);
  901. var dataSource = instance.get('dataSource');
  902. var request = instance.generateRequest(query);
  903. instance.fire('dataRequest', request);
  904. dataSource.sendRequest(request);
  905. },
  906. /**
  907. * Updates in the input element with the first result as the user types,
  908. * selecting the text the user has not typed yet.
  909. *
  910. * @method _typeAhead
  911. * @param {Node} elListItem The selected list item
  912. * @param {String} query The query string
  913. * @protected
  914. */
  915. _typeAhead: function(elListItem, query) {
  916. var instance = this;
  917. if (!instance.get('typeAhead') || KeyMap.isKey(instance._keyCode, BACKSPACE)) {
  918. return;
  919. }
  920. var inputEl = A.Node.getDOMNode(instance.inputNode);
  921. if (inputEl.setSelectionRange || inputEl.createTextRange) {
  922. instance._typeAheadDelayId = setTimeout(
  923. function() {
  924. var value = inputEl.value;
  925. var start = value.length;
  926. instance._updateValue(elListItem);
  927. var end = inputEl.value.length;
  928. instance.inputNode.selectText(start, end);
  929. var prefill = inputEl.value.substr(start, end);
  930. instance.fire('typeAhead', query, prefill);
  931. },
  932. instance.get('typeAheadDelay')
  933. );
  934. }
  935. },
  936. _currentQuery: null,
  937. _initInputValue: null,
  938. _keyCode: null,
  939. _lastValue: null,
  940. _pastSelections: '',
  941. _typeAheadDelayId: -1
  942. };
  943. A.InputTextControl = InputTextControl;
  944. }, '@VERSION@' ,{requires:['aui-base','aui-datasource-control-base','aui-form-combobox']});
  945. AUI.add('aui-datasource-control', function(A){}, '@VERSION@' ,{use:['aui-datasource-control-base','aui-input-text-control'], skinnable:true});