selectlist.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. * Fuel UX Selectlist
  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.selectlist;
  23. // SELECT CONSTRUCTOR AND PROTOTYPE
  24. var Selectlist = function (element, options) {
  25. this.$element = $(element);
  26. this.options = $.extend({}, $.fn.selectlist.defaults, options);
  27. this.$button = this.$element.find('.btn.dropdown-toggle');
  28. this.$hiddenField = this.$element.find('.hidden-field');
  29. this.$label = this.$element.find('.selected-label');
  30. this.$dropdownMenu = this.$element.find('.dropdown-menu');
  31. this.$element.on('click.fu.selectlist', '.dropdown-menu a', $.proxy(this.itemClicked, this));
  32. this.setDefaultSelection();
  33. if (options.resize === 'auto' || this.$element.attr('data-resize') === 'auto') {
  34. this.resize();
  35. }
  36. };
  37. Selectlist.prototype = {
  38. constructor: Selectlist,
  39. destroy: function () {
  40. this.$element.remove();
  41. // any external bindings
  42. // [none]
  43. // empty elements to return to original markup
  44. // [none]
  45. // returns string of markup
  46. return this.$element[0].outerHTML;
  47. },
  48. doSelect: function ($item) {
  49. var $selectedItem;
  50. this.$selectedItem = $selectedItem = $item;
  51. this.$hiddenField.val(this.$selectedItem.attr('data-value'));
  52. this.$label.html($(this.$selectedItem.children()[0]).html());
  53. // clear and set selected item to allow declarative init state
  54. // unlike other controls, selectlist's value is stored internal, not in an input
  55. this.$element.find('li').each(function () {
  56. if ($selectedItem.is($(this))) {
  57. $(this).attr('data-selected', true);
  58. } else {
  59. $(this).removeData('selected').removeAttr('data-selected');
  60. }
  61. });
  62. },
  63. itemClicked: function (e) {
  64. this.$element.trigger('clicked.fu.selectlist', this.$selectedItem);
  65. e.preventDefault();
  66. // is clicked element different from currently selected element?
  67. if (!($(e.target).parent().is(this.$selectedItem))) {
  68. this.itemChanged(e);
  69. }
  70. // return focus to control after selecting an option
  71. this.$element.find('.dropdown-toggle').focus();
  72. },
  73. itemChanged: function (e) {
  74. //selectedItem needs to be <li> since the data is stored there, not in <a>
  75. this.doSelect($(e.target).closest('li'));
  76. // pass object including text and any data-attributes
  77. // to onchange event
  78. var data = this.selectedItem();
  79. // trigger changed event
  80. this.$element.trigger('changed.fu.selectlist', data);
  81. },
  82. resize: function () {
  83. var width = 0;
  84. var newWidth = 0;
  85. var sizer = $('<div/>').addClass('selectlist-sizer');
  86. if (Boolean($(document).find('html').hasClass('fuelux'))) {
  87. // default behavior for fuel ux setup. means fuelux was a class on the html tag
  88. $(document.body).append(sizer);
  89. } else {
  90. // fuelux is not a class on the html tag. So we'll look for the first one we find so the correct styles get applied to the sizer
  91. $('.fuelux:first').append(sizer);
  92. }
  93. sizer.append(this.$element.clone());
  94. this.$element.find('a').each(function () {
  95. sizer.find('.selected-label').text($(this).text());
  96. newWidth = sizer.find('.selectlist').outerWidth();
  97. newWidth = newWidth + sizer.find('.sr-only').outerWidth();
  98. if (newWidth > width) {
  99. width = newWidth;
  100. }
  101. });
  102. if (width <= 1) {
  103. return;
  104. }
  105. this.$button.css('width', width);
  106. this.$dropdownMenu.css('width', width);
  107. sizer.remove();
  108. },
  109. selectedItem: function () {
  110. var txt = this.$selectedItem.text();
  111. return $.extend({
  112. text: txt
  113. }, this.$selectedItem.data());
  114. },
  115. selectByText: function (text) {
  116. var $item = $([]);
  117. this.$element.find('li').each(function () {
  118. if ((this.textContent || this.innerText || $(this).text() || '').toLowerCase() === (text || '').toLowerCase()) {
  119. $item = $(this);
  120. return false;
  121. }
  122. });
  123. this.doSelect($item);
  124. },
  125. selectByValue: function (value) {
  126. var selector = 'li[data-value="' + value + '"]';
  127. this.selectBySelector(selector);
  128. },
  129. selectByIndex: function (index) {
  130. // zero-based index
  131. var selector = 'li:eq(' + index + ')';
  132. this.selectBySelector(selector);
  133. },
  134. selectBySelector: function (selector) {
  135. var $item = this.$element.find(selector);
  136. this.doSelect($item);
  137. },
  138. setDefaultSelection: function () {
  139. var $item = this.$element.find('li[data-selected=true]').eq(0);
  140. if ($item.length === 0) {
  141. $item = this.$element.find('li').has('a').eq(0);
  142. }
  143. this.doSelect($item);
  144. },
  145. enable: function () {
  146. this.$element.removeClass('disabled');
  147. this.$button.removeClass('disabled');
  148. },
  149. disable: function () {
  150. this.$element.addClass('disabled');
  151. this.$button.addClass('disabled');
  152. }
  153. };
  154. // SELECT PLUGIN DEFINITION
  155. $.fn.selectlist = function (option) {
  156. var args = Array.prototype.slice.call(arguments, 1);
  157. var methodReturn;
  158. var $set = this.each(function () {
  159. var $this = $(this);
  160. var data = $this.data('fu.selectlist');
  161. var options = typeof option === 'object' && option;
  162. if (!data) {
  163. $this.data('fu.selectlist', (data = new Selectlist(this, options)));
  164. }
  165. if (typeof option === 'string') {
  166. methodReturn = data[option].apply(data, args);
  167. }
  168. });
  169. return (methodReturn === undefined) ? $set : methodReturn;
  170. };
  171. $.fn.selectlist.defaults = {};
  172. $.fn.selectlist.Constructor = Selectlist;
  173. $.fn.selectlist.noConflict = function () {
  174. $.fn.selectlist = old;
  175. return this;
  176. };
  177. // DATA-API
  178. $(document).on('mousedown.fu.selectlist.data-api', '[data-initialize=selectlist]', function (e) {
  179. var $control = $(e.target).closest('.selectlist');
  180. if (!$control.data('fu.selectlist')) {
  181. $control.selectlist($control.data());
  182. }
  183. });
  184. // Must be domReady for AMD compatibility
  185. $(function () {
  186. $('[data-initialize=selectlist]').each(function () {
  187. var $this = $(this);
  188. if (!$this.data('fu.selectlist')) {
  189. $this.selectlist($this.data());
  190. }
  191. });
  192. });
  193. // -- BEGIN UMD WRAPPER AFTERWORD --
  194. }));
  195. // -- END UMD WRAPPER AFTERWORD --