combobox.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Fuel UX Combobox
  3. * https://github.com/ExactTarget/fuelux
  4. *
  5. * Copyright (c) 2014 ExactTarget
  6. * Licensed under the BSD New license.
  7. */
  8. // -- BEGIN UMD WRAPPER PREFACE --
  9. // For more information on UMD visit:
  10. // https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
  11. (function (factory) {
  12. if (typeof define === 'function' && define.amd) {
  13. // if AMD loader is available, register as an anonymous module.
  14. define(['jquery'], factory);
  15. } else {
  16. // OR use browser globals if AMD is not present
  17. factory(jQuery);
  18. }
  19. }(function ($) {
  20. // -- END UMD WRAPPER PREFACE --
  21. // -- BEGIN MODULE CODE HERE --
  22. var old = $.fn.combobox;
  23. // COMBOBOX CONSTRUCTOR AND PROTOTYPE
  24. var Combobox = function (element, options) {
  25. this.$element = $(element);
  26. this.options = $.extend({}, $.fn.combobox.defaults, options);
  27. this.$dropMenu = this.$element.find('.dropdown-menu');
  28. this.$input = this.$element.find('input');
  29. this.$button = this.$element.find('.btn');
  30. this.$element.on('click.fu.combobox', 'a', $.proxy(this.itemclicked, this));
  31. this.$element.on('change.fu.combobox', 'input', $.proxy(this.inputchanged, this));
  32. this.$element.on('shown.bs.dropdown', $.proxy(this.menuShown, this));
  33. // set default selection
  34. this.setDefaultSelection();
  35. };
  36. Combobox.prototype = {
  37. constructor: Combobox,
  38. destroy: function () {
  39. this.$element.remove();
  40. // remove any external bindings
  41. // [none]
  42. // set input value attrbute in markup
  43. this.$element.find('input').each(function () {
  44. $(this).attr('value', $(this).val());
  45. });
  46. // empty elements to return to original markup
  47. // [none]
  48. return this.$element[0].outerHTML;
  49. },
  50. doSelect: function ($item) {
  51. if (typeof $item[0] !== 'undefined') {
  52. this.$selectedItem = $item;
  53. this.$input.val(this.$selectedItem.text().trim());
  54. } else {
  55. this.$selectedItem = null;
  56. }
  57. },
  58. menuShown: function () {
  59. if (this.options.autoResizeMenu) {
  60. this.resizeMenu();
  61. }
  62. },
  63. resizeMenu: function () {
  64. var width = this.$element.outerWidth();
  65. this.$dropMenu.outerWidth(width);
  66. },
  67. selectedItem: function () {
  68. var item = this.$selectedItem;
  69. var data = {};
  70. if (item) {
  71. var txt = this.$selectedItem.text().trim();
  72. data = $.extend({
  73. text: txt
  74. }, this.$selectedItem.data());
  75. } else {
  76. data = {
  77. text: this.$input.val()
  78. };
  79. }
  80. return data;
  81. },
  82. selectByText: function (text) {
  83. var $item = $([]);
  84. this.$element.find('li').each(function () {
  85. if ((this.textContent || this.innerText || $(this).text() || '').toLowerCase() === (text || '').toLowerCase()) {
  86. $item = $(this);
  87. return false;
  88. }
  89. });
  90. this.doSelect($item);
  91. },
  92. selectByValue: function (value) {
  93. var selector = 'li[data-value="' + value + '"]';
  94. this.selectBySelector(selector);
  95. },
  96. selectByIndex: function (index) {
  97. // zero-based index
  98. var selector = 'li:eq(' + index + ')';
  99. this.selectBySelector(selector);
  100. },
  101. selectBySelector: function (selector) {
  102. var $item = this.$element.find(selector);
  103. this.doSelect($item);
  104. },
  105. setDefaultSelection: function () {
  106. var selector = 'li[data-selected=true]:first';
  107. var item = this.$element.find(selector);
  108. if (item.length > 0) {
  109. // select by data-attribute
  110. this.selectBySelector(selector);
  111. item.removeData('selected');
  112. item.removeAttr('data-selected');
  113. }
  114. },
  115. enable: function () {
  116. this.$element.removeClass('disabled');
  117. this.$input.removeAttr('disabled');
  118. this.$button.removeClass('disabled');
  119. },
  120. disable: function () {
  121. this.$element.addClass('disabled');
  122. this.$input.attr('disabled', true);
  123. this.$button.addClass('disabled');
  124. },
  125. itemclicked: function (e) {
  126. this.$selectedItem = $(e.target).parent();
  127. // set input text and trigger input change event marked as synthetic
  128. this.$input.val(this.$selectedItem.text().trim()).trigger('change', {
  129. synthetic: true
  130. });
  131. // pass object including text and any data-attributes
  132. // to onchange event
  133. var data = this.selectedItem();
  134. // trigger changed event
  135. this.$element.trigger('changed.fu.combobox', data);
  136. e.preventDefault();
  137. // return focus to control after selecting an option
  138. this.$element.find('.dropdown-toggle').focus();
  139. },
  140. inputchanged: function (e, extra) {
  141. // skip processing for internally-generated synthetic event
  142. // to avoid double processing
  143. if (extra && extra.synthetic) return;
  144. var val = $(e.target).val();
  145. this.selectByText(val);
  146. // find match based on input
  147. // if no match, pass the input value
  148. var data = this.selectedItem();
  149. if (data.text.length === 0) {
  150. data = {
  151. text: val
  152. };
  153. }
  154. // trigger changed event
  155. this.$element.trigger('changed.fu.combobox', data);
  156. }
  157. };
  158. // COMBOBOX PLUGIN DEFINITION
  159. $.fn.combobox = function (option) {
  160. var args = Array.prototype.slice.call(arguments, 1);
  161. var methodReturn;
  162. var $set = this.each(function () {
  163. var $this = $(this);
  164. var data = $this.data('fu.combobox');
  165. var options = typeof option === 'object' && option;
  166. if (!data) {
  167. $this.data('fu.combobox', (data = new Combobox(this, options)));
  168. }
  169. if (typeof option === 'string') {
  170. methodReturn = data[option].apply(data, args);
  171. }
  172. });
  173. return (methodReturn === undefined) ? $set : methodReturn;
  174. };
  175. $.fn.combobox.defaults = {
  176. autoResizeMenu: true
  177. };
  178. $.fn.combobox.Constructor = Combobox;
  179. $.fn.combobox.noConflict = function () {
  180. $.fn.combobox = old;
  181. return this;
  182. };
  183. // DATA-API
  184. $(document).on('mousedown.fu.combobox.data-api', '[data-initialize=combobox]', function (e) {
  185. var $control = $(e.target).closest('.combobox');
  186. if (!$control.data('fu.combobox')) {
  187. $control.combobox($control.data());
  188. }
  189. });
  190. // Must be domReady for AMD compatibility
  191. $(function () {
  192. $('[data-initialize=combobox]').each(function () {
  193. var $this = $(this);
  194. if (!$this.data('fu.combobox')) {
  195. $this.combobox($this.data());
  196. }
  197. });
  198. });
  199. // -- BEGIN UMD WRAPPER AFTERWORD --
  200. }));
  201. // -- END UMD WRAPPER AFTERWORD --