tinyselect.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. * tinySelect
  3. *
  4. * Licensed under MIT license.
  5. *
  6. * @version 1.0.4
  7. * @author Pekka Harjamäki
  8. */
  9. ;(function($) {
  10. "use strict";
  11. var TinySelect = {
  12. /* ******************************************************************* *
  13. * Class initializers
  14. * ******************************************************************* */
  15. init: function($el, options) {
  16. $el.data("tinySelectObj",this);
  17. this.config = $.extend({
  18. showSearch: true,
  19. txtLoading: "Loading...",
  20. txtAjaxFailure: "Error...",
  21. dataUrl: null,
  22. dataParser: null
  23. },options);
  24. this.state = {
  25. container: null,
  26. selectBox: null,
  27. itemContainer: null,
  28. searchContainer: null,
  29. searchBox: null,
  30. $el: null,
  31. open: false,
  32. ajaxPending: false,
  33. selectedValue: -1,
  34. originalItemData: [],
  35. filteredItemData: []
  36. };
  37. this.readSelect($el);
  38. this.createSelect($el);
  39. },
  40. createSelect: function($el) {
  41. // Create container for select, search and options
  42. this.state.container = $("<div></div>").
  43. addClass("tinyselect").
  44. css({ width: $el.css("width") });
  45. // Create the select element
  46. this.state.selectBox = $("<div></div>").
  47. addClass("selectbox").
  48. on("click", { self:this }, this.onSelectBoxClicked );
  49. this.state.container.append(this.state.selectBox);
  50. // Create container to hold search and results
  51. this.state.dropdown = $("<div></div>").
  52. addClass("dropdown").
  53. hide();
  54. this.state.container.append(this.state.dropdown);
  55. // Add search as first element
  56. if(this.config.showSearch)
  57. this.createSearch(this.state.dropdown);
  58. // Create ul to hold items
  59. this.state.itemContainer = $("<ul></ul>").
  60. addClass("itemcontainer");
  61. this.state.dropdown.append(this.state.itemContainer);
  62. //
  63. this.createItems();
  64. // Hide original select element and add new component to below
  65. $el.hide().after(this.state.container);
  66. this.state.$el = $el;
  67. // Hide select content when clicked elsewhere in the document
  68. $(document).on("click", {self: this}, this.onDocumentClicked );
  69. },
  70. createItems: function(selected) {
  71. var l1, opt;
  72. // Remove all
  73. this.state.itemContainer.empty();
  74. //
  75. for(l1=0; l1<this.state.filteredItemData.length; l1++)
  76. {
  77. opt = this.state.filteredItemData[l1];
  78. var newLi = $("<li></li>").
  79. text( opt.text ).
  80. addClass( "item" ).
  81. attr( "data-value", opt.val );
  82. if( opt.val == this.state.selectedValue )
  83. {
  84. this.state.selectBox.html( opt.text );
  85. newLi.addClass("selected");
  86. }
  87. newLi.on("click", { self:this }, this.onSelectLiClicked );
  88. this.state.itemContainer.append(newLi);
  89. }
  90. },
  91. createSearch: function($el) {
  92. this.state.searchContainer = $("<div></div>").
  93. addClass("searchcontainer");
  94. this.state.searchBox = $("<input type='text'></input>").
  95. addClass("searchbox").
  96. on("click",function(e) { e.stopPropagation(); }).
  97. on("keyup",{ self: this }, this.onSearchKeyPress);
  98. this.state.searchContainer.append($("<span class='searchicon'></span>"));
  99. this.state.searchContainer.append(this.state.searchBox);
  100. this.state.dropdown.append(this.state.searchContainer);
  101. },
  102. readSelect: function($el) {
  103. var self = this;
  104. $el.find("option").each(function(index){
  105. var opt = $(this);
  106. self.state.originalItemData.push({ val: opt.val() , text: opt.text() });
  107. });
  108. this.state.filteredItemData = this.state.originalItemData;
  109. this.state.selectedValue = $el.val();
  110. },
  111. setAjaxIndicator: function(failure) {
  112. this.state.ajaxPending = true;
  113. this.state.itemContainer.empty();
  114. if(this.state.searchContainer !== null)
  115. this.state.searchContainer.hide();
  116. var newLi = $("<li></li>");
  117. if(!failure)
  118. {
  119. newLi.text( this.config.txtLoading ).
  120. addClass( "loadindicator" );
  121. } else {
  122. newLi.text( this.config.txtAjaxFailure ).
  123. addClass( "loaderrorindicator" );
  124. }
  125. this.state.itemContainer.append(newLi);
  126. },
  127. /* ******************************************************************* *
  128. * Event handlers
  129. * ******************************************************************* */
  130. onDocumentClicked: function(e) {
  131. var self = e.data.self;
  132. if( self.state.open )
  133. self.onSelectBoxClicked(e);
  134. },
  135. onSearchKeyPress: function(e) {
  136. var self = e.data.self,
  137. sval = $(e.currentTarget).val();
  138. if(sval.length === 0)
  139. {
  140. self.state.filteredItemData = self.state.originalItemData;
  141. } else {
  142. self.state.filteredItemData = self.state.originalItemData.filter(function(item){
  143. return item.text.toLowerCase().indexOf(sval) >= 0 ? true: false;
  144. });
  145. }
  146. self.createItems();
  147. },
  148. onSelectBoxClicked: function(e) {
  149. var self = e.data.self;
  150. // Do nothing, if currently animating
  151. if(self.state.dropdown.is(":animated"))
  152. return;
  153. // Close selectBox
  154. if( self.state.open )
  155. {
  156. self.state.open = false;
  157. self.state.selectBox.removeClass("open");
  158. self.state.dropdown.slideUp(100);
  159. return;
  160. }
  161. // Open selectbox
  162. if(self.config.dataUrl !== null)
  163. {
  164. self.setAjaxIndicator(false);
  165. $.ajax({
  166. url: self.config.dataUrl,
  167. dataType: "json",
  168. type: "GET"
  169. }). done( function(data) { self.onAjaxLoadSuccess(self, data); } ).
  170. fail( function(data) { self.onAjaxLoadError(self, data); } );
  171. }
  172. self.state.open = true;
  173. self.state.selectBox.addClass("open");
  174. self.state.dropdown.slideDown(100);
  175. },
  176. onAjaxLoadSuccess: function(self,data) {
  177. self.state.ajaxPending = false;
  178. if(self.config.dataParser !== null )
  179. {
  180. data = self.config.dataParser(data, self.state.selectedValue);
  181. }
  182. self.state.$el.empty();
  183. data.forEach(function(v){
  184. if(v.selected)
  185. self.state.selectedValue = v.val;
  186. self.state.$el.append(
  187. $("<option></option>").
  188. text( v.text ).
  189. val( v.val )
  190. );
  191. });
  192. self.state.$el.val( self.state.selectedValue );
  193. self.state.originalItemData = data;
  194. self.state.filteredItemData = data;
  195. if(this.state.searchContainer !== null)
  196. this.state.searchContainer.show();
  197. self.createItems();
  198. },
  199. onAjaxLoadError: function(self,data) {
  200. self.setAjaxIndicator(true);
  201. },
  202. onSelectLiClicked: function(e) {
  203. var self = e.data.self,
  204. item = $(e.currentTarget);
  205. self.state.dropdown.find("li").each(function() {
  206. $(this).removeClass("selected");
  207. });
  208. item.addClass("selected");
  209. self.state.selectBox.html( item.text() );
  210. self.state.selectedValue = item.attr("data-value");
  211. self.state.$el.val(self.state.selectedValue);
  212. self.state.$el.trigger("change");
  213. },
  214. /* ******************************************************************* *
  215. * External callbacks
  216. * ******************************************************************* */
  217. };
  218. /* ******************************************************************* *
  219. * Plugin main
  220. * ******************************************************************* */
  221. $.fn.tinyselect = function(options) {
  222. if( typeof(options) != "undefined" )
  223. {
  224. }
  225. return this.each(function(){
  226. var sel = Object.create(TinySelect);
  227. sel.init( $(this) , options);
  228. });
  229. };
  230. }(jQuery));