Administrator
2023-04-21 195945efc5db921a4c9eb8cf9421c172273293f5
提交 | 用户 | 时间
58d006 1 /*
A 2  *  Bootstrap Duallistbox - v3.0.5
3  *  A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices.
4  *  http://www.virtuosoft.eu/code/bootstrap-duallistbox/
5  *
6  *  Made by István Ujj-Mészáros
7  *  Under Apache License v2.0 License
8  */
9 ;(function ($, window, document, undefined) {
10   // Create the defaults once
11   var pluginName = 'bootstrapDualListbox',
12     defaults = {
13       bootstrap2Compatible: false,
14       filterTextClear: 'show all',
15       filterPlaceHolder: 'Filter',
16       moveSelectedLabel: 'Move selected',
17       moveAllLabel: 'Move all',
18       removeSelectedLabel: 'Remove selected',
19       removeAllLabel: 'Remove all',
20       moveOnSelect: true,                                                                 // true/false (forced true on androids, see the comment later)
21       preserveSelectionOnMove: false,                                                     // 'all' / 'moved' / false
22       selectedListLabel: false,                                                           // 'string', false
23       nonSelectedListLabel: false,                                                        // 'string', false
24       helperSelectNamePostfix: '_helper',                                                 // 'string_of_postfix' / false
25       selectorMinimalHeight: 100,
26       showFilterInputs: true,                                                             // whether to show filter inputs
27       nonSelectedFilter: '',                                                              // string, filter the non selected options
28       selectedFilter: '',                                                                 // string, filter the selected options
29       infoText: 'Showing all {0}',                                                        // text when all options are visible / false for no info text
30       infoTextFiltered: '<span class="label label-warning">Filtered</span> {0} from {1}', // when not all of the options are visible due to the filter
31       infoTextEmpty: 'Empty list',                                                        // when there are no options present in the list
32       filterOnValues: false                                                               // filter by selector's values, boolean
33       , buttonClass: 'btn-white btn-bold'//ACE
34     },
35     // Selections are invisible on android if the containing select is styled with CSS
36     // http://code.google.com/p/android/issues/detail?id=16922
37     isBuggyAndroid = /android/i.test(navigator.userAgent.toLowerCase());
38
39   // The actual plugin constructor
40   function BootstrapDualListbox(element, options) {
41     this.element = $(element);
42     // jQuery has an extend method which merges the contents of two or
43     // more objects, storing the result in the first object. The first object
44     // is generally empty as we don't want to alter the default options for
45     // future instances of the plugin
46     this.settings = $.extend({}, defaults, options);
47     this._defaults = defaults;
48     this._name = pluginName;
49     this.init();
50   }
51
52   function triggerChangeEvent(dualListbox) {
53     dualListbox.element.trigger('change');
54   }
55
56   function updateSelectionStates(dualListbox) {
57     dualListbox.element.find('option').each(function(index, item) {
58       var $item = $(item);
59       if (typeof($item.data('original-index')) === 'undefined') {
60         $item.data('original-index', dualListbox.elementCount++);
61       }
62       if (typeof($item.data('_selected')) === 'undefined') {
63         $item.data('_selected', false);
64       }
65     });
66   }
67
68   function changeSelectionState(dualListbox, original_index, selected) {
69     dualListbox.element.find('option').each(function(index, item) {
70       var $item = $(item);
71       if ($item.data('original-index') === original_index) {
72         $item.prop('selected', selected);
73       }
74     });
75   }
76
77   function formatString(s, args) {
78     return s.replace(/\{(\d+)\}/g, function(match, number) {
79       return typeof args[number] !== 'undefined' ? args[number] : match;
80     });
81   }
82
83   function refreshInfo(dualListbox) {
84     if (!dualListbox.settings.infoText) {
85       return;
86     }
87
88     var visible1 = dualListbox.elements.select1.find('option').length,
89       visible2 = dualListbox.elements.select2.find('option').length,
90       all1 = dualListbox.element.find('option').length - dualListbox.selectedElements,
91       all2 = dualListbox.selectedElements,
92       content = '';
93
94     if (all1 === 0) {
95       content = dualListbox.settings.infoTextEmpty;
96     } else if (visible1 === all1) {
97       content = formatString(dualListbox.settings.infoText, [visible1, all1]);
98     } else {
99       content = formatString(dualListbox.settings.infoTextFiltered, [visible1, all1]);
100     }
101
102     dualListbox.elements.info1.html(content);
103     dualListbox.elements.box1.toggleClass('filtered', !(visible1 === all1 || all1 === 0));
104
105     if (all2 === 0) {
106       content = dualListbox.settings.infoTextEmpty;
107     } else if (visible2 === all2) {
108       content = formatString(dualListbox.settings.infoText, [visible2, all2]);
109     } else {
110       content = formatString(dualListbox.settings.infoTextFiltered, [visible2, all2]);
111     }
112
113     dualListbox.elements.info2.html(content);
114     dualListbox.elements.box2.toggleClass('filtered', !(visible2 === all2 || all2 === 0));
115   }
116
117   function refreshSelects(dualListbox) {
118     dualListbox.selectedElements = 0;
119
120     dualListbox.elements.select1.empty();
121     dualListbox.elements.select2.empty();
122
123     dualListbox.element.find('option').each(function(index, item) {
124       var $item = $(item);
125       if ($item.prop('selected')) {
126         dualListbox.selectedElements++;
127         dualListbox.elements.select2.append($item.clone(true).prop('selected', $item.data('_selected')));
128       } else {
129         dualListbox.elements.select1.append($item.clone(true).prop('selected', $item.data('_selected')));
130       }
131     });
132
133     if (dualListbox.settings.showFilterInputs) {
134       filter(dualListbox, 1);
135       filter(dualListbox, 2);
136     }
137     refreshInfo(dualListbox);
138   }
139
140   function filter(dualListbox, selectIndex) {
141     if (!dualListbox.settings.showFilterInputs) {
142       return;
143     }
144
145     saveSelections(dualListbox, selectIndex);
146
147     dualListbox.elements['select'+selectIndex].empty().scrollTop(0);
148     var regex = new RegExp($.trim(dualListbox.elements['filterInput'+selectIndex].val()), 'gi'),
149       allOptions = dualListbox.element.find('option'),
150       options = dualListbox.element;
151
152     if (selectIndex === 1) {
153       options = allOptions.not(':selected');
154     } else  {
155       options = options.find('option:selected');
156     }
157
158     options.each(function(index, item) {
159       var $item = $(item),
160         isFiltered = true;
161       if (item.text.match(regex) || (dualListbox.settings.filterOnValues && $item.attr('value').match(regex) ) ) {
162         isFiltered = false;
163         dualListbox.elements['select'+selectIndex].append($item.clone(true).prop('selected', $item.data('_selected')));
164       }
165       allOptions.eq($item.data('original-index')).data('filtered'+selectIndex, isFiltered);
166     });
167
168     refreshInfo(dualListbox);
169   }
170
171   function saveSelections(dualListbox, selectIndex) {
172     var options = dualListbox.element.find('option');
173     dualListbox.elements['select'+selectIndex].find('option').each(function(index, item) {
174       var $item = $(item);
175       options.eq($item.data('original-index')).data('_selected', $item.prop('selected'));
176     });
177   }
178
179   function sortOptions(select) {
180     select.find('option').sort(function(a, b) {
181       return ($(a).data('original-index') > $(b).data('original-index')) ? 1 : -1;
182     }).appendTo(select);
183   }
184
185   function clearSelections(dualListbox) {
186     dualListbox.elements.select1.find('option').each(function() {
187       dualListbox.element.find('option').data('_selected', false);
188     });
189   }
190
191   function move(dualListbox) {
192     if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {
193       saveSelections(dualListbox, 1);
194       saveSelections(dualListbox, 2);
195     } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {
196       saveSelections(dualListbox, 1);
197     }
198
199     dualListbox.elements.select1.find('option:selected').each(function(index, item) {
200       var $item = $(item);
201       if (!$item.data('filtered1')) {
202         changeSelectionState(dualListbox, $item.data('original-index'), true);
203       }
204     });
205
206     refreshSelects(dualListbox);
207     triggerChangeEvent(dualListbox);
208     sortOptions(dualListbox.elements.select2);
209   }
210
211   function remove(dualListbox) {
212     if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {
213       saveSelections(dualListbox, 1);
214       saveSelections(dualListbox, 2);
215     } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {
216       saveSelections(dualListbox, 2);
217     }
218
219     dualListbox.elements.select2.find('option:selected').each(function(index, item) {
220       var $item = $(item);
221       if (!$item.data('filtered2')) {
222         changeSelectionState(dualListbox, $item.data('original-index'), false);
223       }
224     });
225
226     refreshSelects(dualListbox);
227     triggerChangeEvent(dualListbox);
228     sortOptions(dualListbox.elements.select1);
229   }
230
231   function moveAll(dualListbox) {
232     if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {
233       saveSelections(dualListbox, 1);
234       saveSelections(dualListbox, 2);
235     } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {
236       saveSelections(dualListbox, 1);
237     }
238
239     dualListbox.element.find('option').each(function(index, item) {
240       var $item = $(item);
241       if (!$item.data('filtered1')) {
242         $item.prop('selected', true);
243       }
244     });
245
246     refreshSelects(dualListbox);
247     triggerChangeEvent(dualListbox);
248   }
249
250   function removeAll(dualListbox) {
251     if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {
252       saveSelections(dualListbox, 1);
253       saveSelections(dualListbox, 2);
254     } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {
255       saveSelections(dualListbox, 2);
256     }
257
258     dualListbox.element.find('option').each(function(index, item) {
259       var $item = $(item);
260       if (!$item.data('filtered2')) {
261         $item.prop('selected', false);
262       }
263     });
264
265     refreshSelects(dualListbox);
266     triggerChangeEvent(dualListbox);
267   }
268
269   function bindEvents(dualListbox) {
270     dualListbox.elements.form.submit(function(e) {
271       if (dualListbox.elements.filterInput1.is(':focus')) {
272         e.preventDefault();
273         dualListbox.elements.filterInput1.focusout();
274       } else if (dualListbox.elements.filterInput2.is(':focus')) {
275         e.preventDefault();
276         dualListbox.elements.filterInput2.focusout();
277       }
278     });
279
280     dualListbox.element.on('bootstrapDualListbox.refresh', function(e, mustClearSelections){
281       dualListbox.refresh(mustClearSelections);
282     });
283
284     dualListbox.elements.filterClear1.on('click', function() {
285       dualListbox.setNonSelectedFilter('', true);
286     });
287
288     dualListbox.elements.filterClear2.on('click', function() {
289       dualListbox.setSelectedFilter('', true);
290     });
291
292     dualListbox.elements.moveButton.on('click', function() {
293       move(dualListbox);
294     });
295
296     dualListbox.elements.moveAllButton.on('click', function() {
297       moveAll(dualListbox);
298     });
299
300     dualListbox.elements.removeButton.on('click', function() {
301       remove(dualListbox);
302     });
303
304     dualListbox.elements.removeAllButton.on('click', function() {
305       removeAll(dualListbox);
306     });
307
308     dualListbox.elements.filterInput1.on('change keyup', function() {
309       filter(dualListbox, 1);
310     });
311
312     dualListbox.elements.filterInput2.on('change keyup', function() {
313       filter(dualListbox, 2);
314     });
315   }
316
317   BootstrapDualListbox.prototype = {
318     init: function () {
319       // Add the custom HTML template
320       this.container = $('' +
321         '<div class="bootstrap-duallistbox-container">' +
322         ' <div class="box1">' +
323         '   <label></label>' +
324         '   <span class="info-container">' +
325         '     <span class="info"></span>' +
326         '     <button type="button" class="btn clear1 pull-right"></button>' +
327         '   </span>' +
328         '   <input class="filter" type="text">' +
329         '   <div class="btn-group buttons">' +
330         '     <button type="button" class="btn moveall">' +
331         '       <i></i>' +
332         '       <i></i>' +
333         '     </button>' +
334         '     <button type="button" class="btn move">' +
335         '       <i></i>' +
336         '     </button>' +
337         '   </div>' +
338         '   <select multiple="multiple"></select>' +
339         ' </div>' +
340         ' <div class="box2">' +
341         '   <label></label>' +
342         '   <span class="info-container">' +
343         '     <span class="info"></span>' +
344         '     <button type="button" class="btn clear2 pull-right"></button>' +
345         '   </span>' +
346         '   <input class="filter" type="text">' +
347         '   <div class="btn-group buttons">' +
348         '     <button type="button" class="btn remove">' +
349         '       <i></i>' +
350         '     </button>' +
351         '     <button type="button" class="btn removeall">' +
352         '       <i></i>' +
353         '       <i></i>' +
354         '     </button>' +
355         '   </div>' +
356         '   <select multiple="multiple"></select>' +
357         ' </div>' +
358         '</div>')
359         .insertBefore(this.element);
360
361       // Cache the inner elements
362       this.elements = {
363         originalSelect: this.element,
364         box1: $('.box1', this.container),
365         box2: $('.box2', this.container),
366         filterInput1: $('.box1 .filter', this.container),
367         filterInput2: $('.box2 .filter', this.container),
368         filterClear1: $('.box1 .clear1', this.container),
369         filterClear2: $('.box2 .clear2', this.container),
370         label1: $('.box1 > label', this.container),
371         label2: $('.box2 > label', this.container),
372         info1: $('.box1 .info', this.container),
373         info2: $('.box2 .info', this.container),
374         select1: $('.box1 select', this.container),
375         select2: $('.box2 select', this.container),
376         moveButton: $('.box1 .move', this.container),
377         removeButton: $('.box2 .remove', this.container),
378         moveAllButton: $('.box1 .moveall', this.container),
379         removeAllButton: $('.box2 .removeall', this.container),
380         form: $($('.box1 .filter', this.container)[0].form)
381       };
382
383       // Set select IDs
384       this.originalSelectName = this.element.attr('name') || '';
385       var select1Id = 'bootstrap-duallistbox-nonselected-list_' + this.originalSelectName,
386         select2Id = 'bootstrap-duallistbox-selected-list_' + this.originalSelectName;
387       this.elements.select1.attr('id', select1Id);
388       this.elements.select2.attr('id', select2Id);
389       this.elements.label1.attr('for', select1Id);
390       this.elements.label2.attr('for', select2Id);
391
392       // Apply all settings
393       this.selectedElements = 0;
394       this.elementCount = 0;
395       this.setBootstrap2Compatible(this.settings.bootstrap2Compatible);
396       this.setFilterTextClear(this.settings.filterTextClear);
397       this.setFilterPlaceHolder(this.settings.filterPlaceHolder);
398       this.setMoveSelectedLabel(this.settings.moveSelectedLabel);
399       this.setMoveAllLabel(this.settings.moveAllLabel);
400       this.setRemoveSelectedLabel(this.settings.removeSelectedLabel);
401       this.setRemoveAllLabel(this.settings.removeAllLabel);
402       this.setMoveOnSelect(this.settings.moveOnSelect);
403       this.setPreserveSelectionOnMove(this.settings.preserveSelectionOnMove);
404       this.setSelectedListLabel(this.settings.selectedListLabel);
405       this.setNonSelectedListLabel(this.settings.nonSelectedListLabel);
406       this.setHelperSelectNamePostfix(this.settings.helperSelectNamePostfix);
407       this.setSelectOrMinimalHeight(this.settings.selectorMinimalHeight);
408
409       updateSelectionStates(this);
410
411       this.setShowFilterInputs(this.settings.showFilterInputs);
412       this.setNonSelectedFilter(this.settings.nonSelectedFilter);
413       this.setSelectedFilter(this.settings.selectedFilter);
414       this.setInfoText(this.settings.infoText);
415       this.setInfoTextFiltered(this.settings.infoTextFiltered);
416       this.setInfoTextEmpty(this.settings.infoTextEmpty);
417       this.setFilterOnValues(this.settings.filterOnValues);
418
419       // Hide the original select
420       this.element.hide();
421
422       bindEvents(this);
423       refreshSelects(this);
424
425       return this.element;
426     },
427     setBootstrap2Compatible: function(value, refresh) {
428       this.settings.bootstrap2Compatible = value;
429       if (value) {
430         this.container.removeClass('row').addClass('row-fluid bs2compatible');
431         this.container.find('.box1, .box2').removeClass('col-md-6').addClass('span6');
432         this.container.find('.clear1, .clear2').removeClass('btn-default btn-xs').addClass('btn-mini');
433         this.container.find('input, select').removeClass('form-control');
434         this.container.find('.btn').removeClass('btn-default');
435         this.container.find('.moveall > i, .move > i').removeClass('fa fa-arrow-right').addClass('icon-arrow-right');//ACE
436         this.container.find('.removeall > i, .remove > i').removeClass('fa fa-arrow-left').addClass('icon-arrow-left');//ACE
437       } else {
438         this.container.removeClass('row-fluid bs2compatible').addClass('row');
439         this.container.find('.box1, .box2').removeClass('span6').addClass('col-md-6');
440         this.container.find('.clear1, .clear2').removeClass('btn-mini').addClass('btn-default btn-xs');
441         this.container.find('input, select').addClass('form-control');
442         this.container.find('.btn').addClass(this.settings.buttonClass)//ACE;//s.addClass('btn-default');
443         this.container.find('.moveall > i, .move > i').removeClass('icon-arrow-right').addClass('fa fa-arrow-right');//ACE
444         this.container.find('.removeall > i, .remove > i').removeClass('icon-arrow-left').addClass('fa fa-arrow-left');//ACE
445       }
446       if (refresh) {
447         refreshSelects(this);
448       }
449       return this.element;
450     },
451     setFilterTextClear: function(value, refresh) {
452       this.settings.filterTextClear = value;
453       this.elements.filterClear1.html(value);
454       this.elements.filterClear2.html(value);
455       if (refresh) {
456         refreshSelects(this);
457       }
458       return this.element;
459     },
460     setFilterPlaceHolder: function(value, refresh) {
461       this.settings.filterPlaceHolder = value;
462       this.elements.filterInput1.attr('placeholder', value);
463       this.elements.filterInput2.attr('placeholder', value);
464       if (refresh) {
465         refreshSelects(this);
466       }
467       return this.element;
468     },
469     setMoveSelectedLabel: function(value, refresh) {
470       this.settings.moveSelectedLabel = value;
471       this.elements.moveButton.attr('title', value);
472       if (refresh) {
473         refreshSelects(this);
474       }
475       return this.element;
476     },
477     setMoveAllLabel: function(value, refresh) {
478       this.settings.moveAllLabel = value;
479       this.elements.moveAllButton.attr('title', value);
480       if (refresh) {
481         refreshSelects(this);
482       }
483       return this.element;
484     },
485     setRemoveSelectedLabel: function(value, refresh) {
486       this.settings.removeSelectedLabel = value;
487       this.elements.removeButton.attr('title', value);
488       if (refresh) {
489         refreshSelects(this);
490       }
491       return this.element;
492     },
493     setRemoveAllLabel: function(value, refresh) {
494       this.settings.removeAllLabel = value;
495       this.elements.removeAllButton.attr('title', value);
496       if (refresh) {
497         refreshSelects(this);
498       }
499       return this.element;
500     },
501     setMoveOnSelect: function(value, refresh) {
502       if (isBuggyAndroid) {
503         value = true;
504       }
505       this.settings.moveOnSelect = value;
506       if (this.settings.moveOnSelect) {
507         this.container.addClass('moveonselect');
508         var self = this;
509         this.elements.select1.on('change', function() {
510           move(self);
511         });
512         this.elements.select2.on('change', function() {
513           remove(self);
514         });
515       } else {
516         this.container.removeClass('moveonselect');
517         this.elements.select1.off('change');
518         this.elements.select2.off('change');
519       }
520       if (refresh) {
521         refreshSelects(this);
522       }
523       return this.element;
524     },
525     setPreserveSelectionOnMove: function(value, refresh) {
526       // We are forcing to move on select and disabling preserveSelectionOnMove on Android
527       if (isBuggyAndroid) {
528         value = false;
529       }
530       this.settings.preserveSelectionOnMove = value;
531       if (refresh) {
532         refreshSelects(this);
533       }
534       return this.element;
535     },
536     setSelectedListLabel: function(value, refresh) {
537       this.settings.selectedListLabel = value;
538       if (value) {
539         this.elements.label2.show().html(value);
540       } else {
541         this.elements.label2.hide().html(value);
542       }
543       if (refresh) {
544         refreshSelects(this);
545       }
546       return this.element;
547     },
548     setNonSelectedListLabel: function(value, refresh) {
549       this.settings.nonSelectedListLabel = value;
550       if (value) {
551         this.elements.label1.show().html(value);
552       } else {
553         this.elements.label1.hide().html(value);
554       }
555       if (refresh) {
556         refreshSelects(this);
557       }
558       return this.element;
559     },
560     setHelperSelectNamePostfix: function(value, refresh) {
561       this.settings.helperSelectNamePostfix = value;
562       if (value) {
563         this.elements.select1.attr('name', this.originalSelectName + value + '1');
564         this.elements.select2.attr('name', this.originalSelectName + value + '2');
565       } else {
566         this.elements.select1.removeAttr('name');
567         this.elements.select2.removeAttr('name');
568       }
569       if (refresh) {
570         refreshSelects(this);
571       }
572       return this.element;
573     },
574     setSelectOrMinimalHeight: function(value, refresh) {
575       this.settings.selectorMinimalHeight = value;
576       var height = this.element.height();
577       if (this.element.height() < value) {
578         height = value;
579       }
580       this.elements.select1.height(height);
581       this.elements.select2.height(height);
582       if (refresh) {
583         refreshSelects(this);
584       }
585       return this.element;
586     },
587     setShowFilterInputs: function(value, refresh) {
588       if (!value) {
589         this.setNonSelectedFilter('');
590         this.setSelectedFilter('');
591         refreshSelects(this);
592         this.elements.filterInput1.hide();
593         this.elements.filterInput2.hide();
594       } else {
595         this.elements.filterInput1.show();
596         this.elements.filterInput2.show();
597       }
598       this.settings.showFilterInputs = value;
599       if (refresh) {
600         refreshSelects(this);
601       }
602       return this.element;
603     },
604     setNonSelectedFilter: function(value, refresh) {
605       if (this.settings.showFilterInputs) {
606         this.settings.nonSelectedFilter = value;
607         this.elements.filterInput1.val(value);
608         if (refresh) {
609           refreshSelects(this);
610         }
611         return this.element;
612       }
613     },
614     setSelectedFilter: function(value, refresh) {
615       if (this.settings.showFilterInputs) {
616         this.settings.selectedFilter = value;
617         this.elements.filterInput2.val(value);
618         if (refresh) {
619           refreshSelects(this);
620         }
621         return this.element;
622       }
623     },
624     setInfoText: function(value, refresh) {
625       this.settings.infoText = value;
626       if (refresh) {
627         refreshSelects(this);
628       }
629       return this.element;
630     },
631     setInfoTextFiltered: function(value, refresh) {
632       this.settings.infoTextFiltered = value;
633       if (refresh) {
634         refreshSelects(this);
635       }
636       return this.element;
637     },
638     setInfoTextEmpty: function(value, refresh) {
639       this.settings.infoTextEmpty = value;
640       if (refresh) {
641         refreshSelects(this);
642       }
643       return this.element;
644     },
645     setFilterOnValues: function(value, refresh) {
646       this.settings.filterOnValues = value;
647       if (refresh) {
648         refreshSelects(this);
649       }
650       return this.element;
651     },
652     getContainer: function() {
653       return this.container;
654     },
655     refresh: function(mustClearSelections) {
656       updateSelectionStates(this);
657
658       if (!mustClearSelections) {
659         saveSelections(this, 1);
660         saveSelections(this, 2);
661       } else {
662         clearSelections(this);
663       }
664
665       refreshSelects(this);
666     },
667     destroy: function() {
668       this.container.remove();
669       this.element.show();
670       $.data(this, 'plugin_' + pluginName, null);
671       return this.element;
672     }
673   };
674
675   // A really lightweight plugin wrapper around the constructor,
676   // preventing against multiple instantiations
677   $.fn[ pluginName ] = function (options) {
678     var args = arguments;
679
680     // Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin.
681     if (options === undefined || typeof options === 'object') {
682       return this.each(function () {
683         // If this is not a select
684         if (!$(this).is('select')) {
685           $(this).find('select').each(function(index, item) {
686             // For each nested select, instantiate the Dual List Box
687             $(item).bootstrapDualListbox(options);
688           });
689         } else if (!$.data(this, 'plugin_' + pluginName)) {
690           // Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet
691
692           // if it has no instance, create a new one, pass options to our plugin constructor,
693           // and store the plugin instance in the elements jQuery data object.
694           $.data(this, 'plugin_' + pluginName, new BootstrapDualListbox(this, options));
695         }
696       });
697       // If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function,
698       // treat this as a call to a public method.
699     } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
700
701       // Cache the method call to make it possible to return a value
702       var returns;
703
704       this.each(function () {
705         var instance = $.data(this, 'plugin_' + pluginName);
706         // Tests that there's already a plugin-instance and checks that the requested public method exists
707         if (instance instanceof BootstrapDualListbox && typeof instance[options] === 'function') {
708           // Call the method of our plugin instance, and pass it the supplied arguments.
709           returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
710         }
711       });
712
713       // If the earlier cached method gives a value back return the value,
714       // otherwise return this to preserve chainability.
715       return returns !== undefined ? returns : this;
716     }
717
718   };
719
720 })(jQuery, window, document);