PageRenderTime 22ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/files/free-jqgrid/4.12.1/plugins/ui.multiselect.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 353 lines | 270 code | 34 blank | 49 comment | 34 complexity | ba219123ed8378573e06d41e088bdd95 MD5 | raw file
  1. /**
  2. * @license jQuery UI Multiselect
  3. *
  4. * Authors:
  5. * Michael Aufreiter (quasipartikel.at)
  6. * Yanick Rochon (yanick.rochon[at]gmail[dot]com)
  7. *
  8. * Dual licensed under the MIT (MIT-LICENSE.txt)
  9. * and GPL (GPL-LICENSE.txt) licenses.
  10. *
  11. * http://www.quasipartikel.at/multiselect/
  12. *
  13. *
  14. * Depends:
  15. * ui.core.js
  16. * ui.sortable.js
  17. *
  18. * Optional:
  19. * localization (http://plugins.jquery.com/project/localisation)
  20. * scrollTo (http://plugins.jquery.com/project/ScrollTo)
  21. *
  22. * Todo:
  23. * Make batch actions faster
  24. * Implement dynamic insertion through remote calls
  25. */
  26. (function (factory) {
  27. "use strict";
  28. if (typeof define === "function" && define.amd) {
  29. // AMD. Register as an anonymous module.
  30. define(["jquery", "./jqdnr", "./jqmodal"], factory);
  31. } else if (typeof exports === "object") {
  32. // Node/CommonJS
  33. factory(require("jquery"));
  34. } else {
  35. // Browser globals
  36. factory(jQuery);
  37. }
  38. }(function($) {
  39. $.widget("ui.multiselect", {
  40. options: {
  41. sortable: true,
  42. searchable: true,
  43. doubleClickable: true,
  44. animated: 'fast',
  45. show: 'slideDown',
  46. hide: 'slideUp',
  47. dividerLocation: 0.6,
  48. availableFirst: false,
  49. nodeComparator: function(node1,node2) {
  50. var text1 = node1.text(),
  51. text2 = node2.text();
  52. return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
  53. }
  54. },
  55. _create: function() {
  56. this.element.hide();
  57. this.id = this.element.attr("id");
  58. this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
  59. this.count = 0; // number of currently selected options
  60. this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container);
  61. this.availableContainer = $('<div class="available"></div>')[this.options.availableFirst?'prependTo': 'appendTo'](this.container);
  62. this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 '+$.ui.multiselect.locale.itemsCount+'</span><a href="#" class="remove-all">'+$.ui.multiselect.locale.removeAll+'</a></div>').appendTo(this.selectedContainer);
  63. this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" class="add-all">'+$.ui.multiselect.locale.addAll+'</a></div>').appendTo(this.availableContainer);
  64. this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer);
  65. this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer);
  66. var that = this;
  67. // set dimensions
  68. this.container.width(this.element.width()+1);
  69. this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation));
  70. this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation)));
  71. // fix list height to match <option> depending on their individual header's heights
  72. this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1));
  73. this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1));
  74. if ( !this.options.animated ) {
  75. this.options.show = 'show';
  76. this.options.hide = 'hide';
  77. }
  78. // init lists
  79. this._populateLists(this.element.find('option'));
  80. // make selection sortable
  81. if (this.options.sortable) {
  82. this.selectedList.sortable({
  83. placeholder: 'ui-state-highlight',
  84. axis: 'y',
  85. update: function(event, ui) {
  86. // apply the new sort order to the original selectbox
  87. that.selectedList.find('li').each(function() {
  88. if ($(this).data('optionLink'))
  89. $(this).data('optionLink').remove().appendTo(that.element);
  90. });
  91. },
  92. receive: function(event, ui) {
  93. ui.item.data('optionLink').attr('selected', true);
  94. // increment count
  95. that.count += 1;
  96. that._updateCount();
  97. // workaround, because there's no way to reference
  98. // the new element, see http://dev.jqueryui.com/ticket/4303
  99. that.selectedList.children('.ui-draggable').each(function() {
  100. $(this).removeClass('ui-draggable');
  101. $(this).data('optionLink', ui.item.data('optionLink'));
  102. $(this).data('idx', ui.item.data('idx'));
  103. that._applyItemState($(this), true);
  104. });
  105. // workaround according to http://dev.jqueryui.com/ticket/4088
  106. setTimeout(function() { ui.item.remove(); }, 1);
  107. }
  108. });
  109. }
  110. // set up livesearch
  111. if (this.options.searchable) {
  112. this._registerSearchEvents(this.availableContainer.find('input.search'));
  113. } else {
  114. $('.search').hide();
  115. }
  116. // batch actions
  117. this.container.find(".remove-all").click(function() {
  118. that._populateLists(that.element.find('option').removeAttr('selected'));
  119. return false;
  120. });
  121. this.container.find(".add-all").click(function() {
  122. var options = that.element.find('option').not("[selected]");
  123. if (that.availableList.children('li:hidden').length > 1) {
  124. that.availableList.children('li').each(function(i) {
  125. if ($(this).is(":visible")) $(options[i-1]).attr('selected', 'selected');
  126. });
  127. } else {
  128. options.attr('selected', 'selected');
  129. }
  130. that._populateLists(that.element.find('option'));
  131. return false;
  132. });
  133. },
  134. destroy: function() {
  135. this.element.show();
  136. this.container.remove();
  137. $.Widget.prototype.destroy.apply(this, arguments);
  138. },
  139. _populateLists: function(options) {
  140. this.selectedList.children('.ui-element').remove();
  141. this.availableList.children('.ui-element').remove();
  142. this.count = 0;
  143. var that = this;
  144. var items = $(options.map(function(i) {
  145. var isSelected = $(this).is("[selected]"), item = that._getOptionNode(this).appendTo(isSelected ? that.selectedList : that.availableList).show();
  146. if (isSelected) that.count += 1;
  147. that._applyItemState(item, isSelected);
  148. item.data('idx', i);
  149. return item[0];
  150. }));
  151. // update count
  152. this._updateCount();
  153. that._filter.apply(this.availableContainer.find('input.search'), [that.availableList]);
  154. },
  155. _updateCount: function() {
  156. this.element.trigger('change');
  157. this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount);
  158. },
  159. _getOptionNode: function(option) {
  160. option = $(option);
  161. var node = $('<li class="ui-state-default ui-element" title="'+(option.attr("title") || option.text())+'"><span class="ui-icon"/>'+option.text()+'<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
  162. node.data('optionLink', option);
  163. return node;
  164. },
  165. // clones an item with associated data
  166. // didn't find a smarter away around this
  167. _cloneWithData: function(clonee) {
  168. var clone = clonee.clone(false,false);
  169. clone.data('optionLink', clonee.data('optionLink'));
  170. clone.data('idx', clonee.data('idx'));
  171. return clone;
  172. },
  173. _setSelected: function(item, selected) {
  174. item.data('optionLink').attr('selected', selected);
  175. if (selected) {
  176. var selectedItem = this._cloneWithData(item);
  177. item[this.options.hide](this.options.animated, function() { $(this).remove(); });
  178. selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
  179. this._applyItemState(selectedItem, true);
  180. return selectedItem;
  181. } else {
  182. // look for successor based on initial option index
  183. var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
  184. var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));
  185. // TODO: test needed for dynamic list populating
  186. if ( direction ) {
  187. while (i>=0 && i<items.length) {
  188. direction > 0 ? i++ : i--;
  189. if ( direction != comparator(item, $(items[i])) ) {
  190. // going up, go back one item down, otherwise leave as is
  191. succ = items[direction > 0 ? i : i+1];
  192. break;
  193. }
  194. }
  195. } else {
  196. succ = items[i];
  197. }
  198. var availableItem = this._cloneWithData(item);
  199. succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
  200. item[this.options.hide](this.options.animated, function() { $(this).remove(); });
  201. availableItem.hide()[this.options.show](this.options.animated);
  202. this._applyItemState(availableItem, false);
  203. return availableItem;
  204. }
  205. },
  206. _applyItemState: function(item, selected) {
  207. if (selected) {
  208. if (this.options.sortable)
  209. item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
  210. else
  211. item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
  212. item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
  213. this._registerRemoveEvents(item.find('a.action'));
  214. } else {
  215. item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
  216. item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
  217. this._registerAddEvents(item.find('a.action'));
  218. }
  219. this._registerDoubleClickEvents(item);
  220. this._registerHoverEvents(item);
  221. },
  222. // taken from John Resig's liveUpdate script
  223. _filter: function(list) {
  224. var input = $(this);
  225. var rows = list.children('li'),
  226. cache = rows.map(function(){
  227. return $(this).text().toLowerCase();
  228. });
  229. var term = $.trim(input.val().toLowerCase()), scores = [];
  230. if (!term) {
  231. rows.show();
  232. } else {
  233. rows.hide();
  234. cache.each(function(i) {
  235. if (this.indexOf(term)>-1) { scores.push(i); }
  236. });
  237. $.each(scores, function() {
  238. $(rows[this]).show();
  239. });
  240. }
  241. },
  242. _registerDoubleClickEvents: function(elements) {
  243. if (!this.options.doubleClickable) return;
  244. elements.dblclick(function(ev) {
  245. if ($(ev.target).closest('.action').length === 0) {
  246. // This may be triggered with rapid clicks on actions as well. In that
  247. // case don't trigger an additional click.
  248. elements.find('a.action').click();
  249. }
  250. });
  251. },
  252. _registerHoverEvents: function(elements) {
  253. elements.removeClass('ui-state-hover');
  254. elements.mouseover(function() {
  255. $(this).addClass('ui-state-hover');
  256. });
  257. elements.mouseout(function() {
  258. $(this).removeClass('ui-state-hover');
  259. });
  260. },
  261. _registerAddEvents: function(elements) {
  262. var that = this;
  263. elements.click(function() {
  264. var item = that._setSelected($(this).parent(), true);
  265. that.count += 1;
  266. that._updateCount();
  267. return false;
  268. });
  269. // make draggable
  270. if (this.options.sortable) {
  271. elements.each(function() {
  272. $(this).parent().draggable({
  273. connectToSortable: that.selectedList,
  274. helper: function() {
  275. var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
  276. selectedItem.width($(this).width());
  277. return selectedItem;
  278. },
  279. appendTo: that.container,
  280. containment: that.container,
  281. revert: 'invalid'
  282. });
  283. });
  284. }
  285. },
  286. _registerRemoveEvents: function(elements) {
  287. var that = this;
  288. elements.click(function() {
  289. that._setSelected($(this).parent(), false);
  290. that.count -= 1;
  291. that._updateCount();
  292. return false;
  293. });
  294. },
  295. _registerSearchEvents: function(input) {
  296. var that = this;
  297. input.focus(function() {
  298. $(this).addClass('ui-state-active');
  299. })
  300. .blur(function() {
  301. $(this).removeClass('ui-state-active');
  302. })
  303. .keypress(function(e) {
  304. if (e.keyCode == 13)
  305. return false;
  306. })
  307. .keyup(function() {
  308. that._filter.apply(this, [that.availableList]);
  309. });
  310. }
  311. });
  312. $.extend($.ui.multiselect, {
  313. locale: {
  314. addAll:'Add all',
  315. removeAll:'Remove all',
  316. itemsCount:'items selected'
  317. }
  318. });
  319. }));