View as "text/plain" | Blame | Last modification | View Log | RSS feed
/** Bootstrap Duallistbox - v3.0.9* A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices.* https://www.virtuosoft.eu/code/bootstrap-duallistbox/** Made by István Ujj-Mészáros* Under Apache License v2.0 License*/;(function ($, window, document, undefined) {// Create the defaults oncevar pluginName = 'bootstrapDualListbox',defaults = {bootstrap2Compatible: false,filterTextClear: 'show all',filterPlaceHolder: 'Filter',moveSelectedLabel: 'Move selected',moveAllLabel: 'Move all',removeSelectedLabel: 'Remove selected',removeAllLabel: 'Remove all',moveOnSelect: true, // true/false (forced true on androids, see the comment later)moveOnDoubleClick: true, // true/false (forced false on androids, cause moveOnSelect is forced to true)preserveSelectionOnMove: false, // 'all' / 'moved' / falseselectedListLabel: false, // 'string', falsenonSelectedListLabel: false, // 'string', falsehelperSelectNamePostfix: '_helper', // 'string_of_postfix' / falseselectorMinimalHeight: 100,showFilterInputs: true, // whether to show filter inputsnonSelectedFilter: '', // string, filter the non selected optionsselectedFilter: '', // string, filter the selected optionsinfoText: 'Showing all {0}', // text when all options are visible / false for no info textinfoTextFiltered: '<span class="label label-warning">Filtered</span> {0} from {1}', // when not all of the options are visible due to the filterinfoTextEmpty: 'Empty list', // when there are no options present in the listfilterOnValues: false, // filter by selector's values, booleansortByInputOrder: false,eventMoveOverride: false, // boolean, allows user to unbind default event behaviour and run their own insteadeventMoveAllOverride: false, // boolean, allows user to unbind default event behaviour and run their own insteadeventRemoveOverride: false, // boolean, allows user to unbind default event behaviour and run their own insteadeventRemoveAllOverride: false // boolean, allows user to unbind default event behaviour and run their own instead},// Selections are invisible on android if the containing select is styled with CSS// http://code.google.com/p/android/issues/detail?id=16922isBuggyAndroid = /android/i.test(navigator.userAgent.toLowerCase());// The actual plugin constructorfunction BootstrapDualListbox(element, options) {this.element = $(element);// jQuery has an extend method which merges the contents of two or// more objects, storing the result in the first object. The first object// is generally empty as we don't want to alter the default options for// future instances of the pluginthis.settings = $.extend({}, defaults, options);this._defaults = defaults;this._name = pluginName;this.init();}function triggerChangeEvent(dualListbox) {dualListbox.element.trigger('change');}function updateSelectionStates(dualListbox) {dualListbox.element.find('option').each(function(index, item) {var $item = $(item);if (typeof($item.data('original-index')) === 'undefined') {$item.data('original-index', dualListbox.elementCount++);}if (typeof($item.data('_selected')) === 'undefined') {$item.data('_selected', false);}});}function changeSelectionState(dualListbox, original_index, selected) {dualListbox.element.find('option').each(function(index, item) {var $item = $(item);if ($item.data('original-index') === original_index) {$item.prop('selected', selected);if(selected){$item.attr('data-sortindex', dualListbox.sortIndex);dualListbox.sortIndex++;} else {$item.removeAttr('data-sortindex');}}});}function formatString(s, args) {return s.replace(/\{(\d+)\}/g, function(match, number) {return typeof args[number] !== 'undefined' ? args[number] : match;});}function refreshInfo(dualListbox) {if (!dualListbox.settings.infoText) {return;}var visible1 = dualListbox.elements.select1.find('option').length,visible2 = dualListbox.elements.select2.find('option').length,all1 = dualListbox.element.find('option').length - dualListbox.selectedElements,all2 = dualListbox.selectedElements,content = '';if (all1 === 0) {content = dualListbox.settings.infoTextEmpty;} else if (visible1 === all1) {content = formatString(dualListbox.settings.infoText, [visible1, all1]);} else {content = formatString(dualListbox.settings.infoTextFiltered, [visible1, all1]);}dualListbox.elements.info1.html(content);dualListbox.elements.box1.toggleClass('filtered', !(visible1 === all1 || all1 === 0));if (all2 === 0) {content = dualListbox.settings.infoTextEmpty;} else if (visible2 === all2) {content = formatString(dualListbox.settings.infoText, [visible2, all2]);} else {content = formatString(dualListbox.settings.infoTextFiltered, [visible2, all2]);}dualListbox.elements.info2.html(content);dualListbox.elements.box2.toggleClass('filtered', !(visible2 === all2 || all2 === 0));}function refreshSelects(dualListbox) {dualListbox.selectedElements = 0;dualListbox.elements.select1.empty();dualListbox.elements.select2.empty();dualListbox.element.find('option').each(function(index, item) {var $item = $(item);if ($item.prop('selected')) {dualListbox.selectedElements++;dualListbox.elements.select2.append($item.clone(true).prop('selected', $item.data('_selected')));} else {dualListbox.elements.select1.append($item.clone(true).prop('selected', $item.data('_selected')));}});if (dualListbox.settings.showFilterInputs) {filter(dualListbox, 1);filter(dualListbox, 2);}refreshInfo(dualListbox);}function filter(dualListbox, selectIndex) {if (!dualListbox.settings.showFilterInputs) {return;}saveSelections(dualListbox, selectIndex);dualListbox.elements['select'+selectIndex].empty().scrollTop(0);var regex = new RegExp($.trim(dualListbox.elements['filterInput'+selectIndex].val()), 'gi'),allOptions = dualListbox.element.find('option'),options = dualListbox.element;if (selectIndex === 1) {options = allOptions.not(':selected');} else {options = options.find('option:selected');}options.each(function(index, item) {var $item = $(item),isFiltered = true;if (item.text.match(regex) || (dualListbox.settings.filterOnValues && $item.attr('value').match(regex) ) ) {isFiltered = false;dualListbox.elements['select'+selectIndex].append($item.clone(true).prop('selected', $item.data('_selected')));}allOptions.eq($item.data('original-index')).data('filtered'+selectIndex, isFiltered);});refreshInfo(dualListbox);}function saveSelections(dualListbox, selectIndex) {var options = dualListbox.element.find('option');dualListbox.elements['select'+selectIndex].find('option').each(function(index, item) {var $item = $(item);options.eq($item.data('original-index')).data('_selected', $item.prop('selected'));});}function sortOptionsByInputOrder(select){var selectopt = select.children('option');selectopt.sort(function(a,b){var an = parseInt(a.getAttribute('data-sortindex')),bn = parseInt(b.getAttribute('data-sortindex'));if(an > bn) {return 1;}if(an < bn) {return -1;}return 0;});selectopt.detach().appendTo(select);}function sortOptions(select, dualListbox) {select.find('option').sort(function(a, b) {return ($(a).data('original-index') > $(b).data('original-index')) ? 1 : -1;}).appendTo(select);// workaround for chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1072475refreshSelects(dualListbox);}function clearSelections(dualListbox) {dualListbox.elements.select1.find('option').each(function() {dualListbox.element.find('option').data('_selected', false);});}function move(dualListbox) {if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);saveSelections(dualListbox, 2);} else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);}dualListbox.elements.select1.find('option:selected').each(function(index, item) {var $item = $(item);if (!$item.data('filtered1')) {changeSelectionState(dualListbox, $item.data('original-index'), true);}});refreshSelects(dualListbox);triggerChangeEvent(dualListbox);if(dualListbox.settings.sortByInputOrder){sortOptionsByInputOrder(dualListbox.elements.select2);} else {sortOptions(dualListbox.elements.select2, dualListbox);}}function remove(dualListbox) {if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);saveSelections(dualListbox, 2);} else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 2);}dualListbox.elements.select2.find('option:selected').each(function(index, item) {var $item = $(item);if (!$item.data('filtered2')) {changeSelectionState(dualListbox, $item.data('original-index'), false);}});refreshSelects(dualListbox);triggerChangeEvent(dualListbox);sortOptions(dualListbox.elements.select1, dualListbox);if(dualListbox.settings.sortByInputOrder){sortOptionsByInputOrder(dualListbox.elements.select2);}}function moveAll(dualListbox) {if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);saveSelections(dualListbox, 2);} else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);}dualListbox.element.find('option').each(function(index, item) {var $item = $(item);if (!$item.data('filtered1')) {$item.prop('selected', true);$item.attr('data-sortindex', dualListbox.sortIndex);dualListbox.sortIndex++;}});refreshSelects(dualListbox);triggerChangeEvent(dualListbox);}function removeAll(dualListbox) {if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 1);saveSelections(dualListbox, 2);} else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) {saveSelections(dualListbox, 2);}dualListbox.element.find('option').each(function(index, item) {var $item = $(item);if (!$item.data('filtered2')) {$item.prop('selected', false);$item.removeAttr('data-sortindex');}});refreshSelects(dualListbox);triggerChangeEvent(dualListbox);}function bindEvents(dualListbox) {dualListbox.elements.form.submit(function(e) {if (dualListbox.elements.filterInput1.is(':focus')) {e.preventDefault();dualListbox.elements.filterInput1.focusout();} else if (dualListbox.elements.filterInput2.is(':focus')) {e.preventDefault();dualListbox.elements.filterInput2.focusout();}});dualListbox.element.on('bootstrapDualListbox.refresh', function(e, mustClearSelections){dualListbox.refresh(mustClearSelections);});dualListbox.elements.filterClear1.on('click', function() {dualListbox.setNonSelectedFilter('', true);});dualListbox.elements.filterClear2.on('click', function() {dualListbox.setSelectedFilter('', true);});if (dualListbox.settings.eventMoveOverride === false) {dualListbox.elements.moveButton.on('click', function() {move(dualListbox);});}if (dualListbox.settings.eventMoveAllOverride === false) {dualListbox.elements.moveAllButton.on('click', function() {moveAll(dualListbox);});}if (dualListbox.settings.eventRemoveOverride === false) {dualListbox.elements.removeButton.on('click', function() {remove(dualListbox);});}if (dualListbox.settings.eventRemoveAllOverride === false) {dualListbox.elements.removeAllButton.on('click', function() {removeAll(dualListbox);});}dualListbox.elements.filterInput1.on('change keyup', function() {filter(dualListbox, 1);});dualListbox.elements.filterInput2.on('change keyup', function() {filter(dualListbox, 2);});}BootstrapDualListbox.prototype = {init: function () {// Add the custom HTML templatethis.container = $('' +'<div class="bootstrap-duallistbox-container">' +' <div class="box1">' +' <label></label>' +' <span class="info-container">' +' <span class="info"></span>' +' <button type="button" class="btn clear1 pull-right"></button>' +' </span>' +' <input class="filter" type="text">' +' <div class="btn-group buttons">' +' <button type="button" class="btn moveall">' +' <i></i>' +' <i></i>' +' </button>' +' <button type="button" class="btn move">' +' <i></i>' +' </button>' +' </div>' +' <select multiple="multiple"></select>' +' </div>' +' <div class="box2">' +' <label></label>' +' <span class="info-container">' +' <span class="info"></span>' +' <button type="button" class="btn clear2 pull-right"></button>' +' </span>' +' <input class="filter" type="text">' +' <div class="btn-group buttons">' +' <button type="button" class="btn remove">' +' <i></i>' +' </button>' +' <button type="button" class="btn removeall">' +' <i></i>' +' <i></i>' +' </button>' +' </div>' +' <select multiple="multiple"></select>' +' </div>' +'</div>').insertBefore(this.element);// Cache the inner elementsthis.elements = {originalSelect: this.element,box1: $('.box1', this.container),box2: $('.box2', this.container),filterInput1: $('.box1 .filter', this.container),filterInput2: $('.box2 .filter', this.container),filterClear1: $('.box1 .clear1', this.container),filterClear2: $('.box2 .clear2', this.container),label1: $('.box1 > label', this.container),label2: $('.box2 > label', this.container),info1: $('.box1 .info', this.container),info2: $('.box2 .info', this.container),select1: $('.box1 select', this.container),select2: $('.box2 select', this.container),moveButton: $('.box1 .move', this.container),removeButton: $('.box2 .remove', this.container),moveAllButton: $('.box1 .moveall', this.container),removeAllButton: $('.box2 .removeall', this.container),form: $($('.box1 .filter', this.container)[0].form)};// Set select IDsthis.originalSelectName = this.element.attr('name') || '';var select1Id = 'bootstrap-duallistbox-nonselected-list_' + this.originalSelectName,select2Id = 'bootstrap-duallistbox-selected-list_' + this.originalSelectName;this.elements.select1.attr('id', select1Id);this.elements.select2.attr('id', select2Id);this.elements.label1.attr('for', select1Id);this.elements.label2.attr('for', select2Id);// Apply all settingsthis.selectedElements = 0;this.sortIndex = 0;this.elementCount = 0;this.setBootstrap2Compatible(this.settings.bootstrap2Compatible);this.setFilterTextClear(this.settings.filterTextClear);this.setFilterPlaceHolder(this.settings.filterPlaceHolder);this.setMoveSelectedLabel(this.settings.moveSelectedLabel);this.setMoveAllLabel(this.settings.moveAllLabel);this.setRemoveSelectedLabel(this.settings.removeSelectedLabel);this.setRemoveAllLabel(this.settings.removeAllLabel);this.setMoveOnSelect(this.settings.moveOnSelect);this.setMoveOnDoubleClick(this.settings.moveOnDoubleClick);this.setPreserveSelectionOnMove(this.settings.preserveSelectionOnMove);this.setSelectedListLabel(this.settings.selectedListLabel);this.setNonSelectedListLabel(this.settings.nonSelectedListLabel);this.setHelperSelectNamePostfix(this.settings.helperSelectNamePostfix);this.setSelectOrMinimalHeight(this.settings.selectorMinimalHeight);updateSelectionStates(this);this.setShowFilterInputs(this.settings.showFilterInputs);this.setNonSelectedFilter(this.settings.nonSelectedFilter);this.setSelectedFilter(this.settings.selectedFilter);this.setInfoText(this.settings.infoText);this.setInfoTextFiltered(this.settings.infoTextFiltered);this.setInfoTextEmpty(this.settings.infoTextEmpty);this.setFilterOnValues(this.settings.filterOnValues);this.setSortByInputOrder(this.settings.sortByInputOrder);this.setEventMoveOverride(this.settings.eventMoveOverride);this.setEventMoveAllOverride(this.settings.eventMoveAllOverride);this.setEventRemoveOverride(this.settings.eventRemoveOverride);this.setEventRemoveAllOverride(this.settings.eventRemoveAllOverride);// Hide the original selectthis.element.hide();bindEvents(this);refreshSelects(this);return this.element;},setBootstrap2Compatible: function(value, refresh) {this.settings.bootstrap2Compatible = value;if (value) {this.container.removeClass('row').addClass('row-fluid bs2compatible');this.container.find('.box1, .box2').removeClass('col-md-6').addClass('span6');this.container.find('.clear1, .clear2').removeClass('btn-default btn-xs').addClass('btn-mini');this.container.find('input, select').removeClass('form-control');this.container.find('.btn').removeClass('btn-default');this.container.find('.moveall > i, .move > i').removeClass('glyphicon glyphicon-arrow-right').addClass('icon-arrow-right');this.container.find('.removeall > i, .remove > i').removeClass('glyphicon glyphicon-arrow-left').addClass('icon-arrow-left');} else {this.container.removeClass('row-fluid bs2compatible').addClass('row');this.container.find('.box1, .box2').removeClass('span6').addClass('col-md-6');this.container.find('.clear1, .clear2').removeClass('btn-mini').addClass('btn-default btn-xs');this.container.find('input, select').addClass('form-control');this.container.find('.btn').addClass('btn-default');this.container.find('.moveall > i, .move > i').removeClass('icon-arrow-right').addClass('glyphicon glyphicon-arrow-right');this.container.find('.removeall > i, .remove > i').removeClass('icon-arrow-left').addClass('glyphicon glyphicon-arrow-left');}if (refresh) {refreshSelects(this);}return this.element;},setFilterTextClear: function(value, refresh) {this.settings.filterTextClear = value;this.elements.filterClear1.html(value);this.elements.filterClear2.html(value);if (refresh) {refreshSelects(this);}return this.element;},setFilterPlaceHolder: function(value, refresh) {this.settings.filterPlaceHolder = value;this.elements.filterInput1.attr('placeholder', value);this.elements.filterInput2.attr('placeholder', value);if (refresh) {refreshSelects(this);}return this.element;},setMoveSelectedLabel: function(value, refresh) {this.settings.moveSelectedLabel = value;this.elements.moveButton.attr('title', value);if (refresh) {refreshSelects(this);}return this.element;},setMoveAllLabel: function(value, refresh) {this.settings.moveAllLabel = value;this.elements.moveAllButton.attr('title', value);if (refresh) {refreshSelects(this);}return this.element;},setRemoveSelectedLabel: function(value, refresh) {this.settings.removeSelectedLabel = value;this.elements.removeButton.attr('title', value);if (refresh) {refreshSelects(this);}return this.element;},setRemoveAllLabel: function(value, refresh) {this.settings.removeAllLabel = value;this.elements.removeAllButton.attr('title', value);if (refresh) {refreshSelects(this);}return this.element;},setMoveOnSelect: function(value, refresh) {if (isBuggyAndroid) {value = true;}this.settings.moveOnSelect = value;if (this.settings.moveOnSelect) {this.container.addClass('moveonselect');var self = this;this.elements.select1.on('change', function() {move(self);});this.elements.select2.on('change', function() {remove(self);});} else {this.container.removeClass('moveonselect');this.elements.select1.off('change');this.elements.select2.off('change');}if (refresh) {refreshSelects(this);}return this.element;},setMoveOnDoubleClick: function(value, refresh) {if (isBuggyAndroid) {value = false;}this.settings.moveOnDoubleClick = value;if (this.settings.moveOnDoubleClick) {this.container.addClass('moveondoubleclick');var self = this;this.elements.select1.on('dblclick', function() {move(self);});this.elements.select2.on('dblclick', function() {remove(self);});} else {this.container.removeClass('moveondoubleclick');this.elements.select1.off('dblclick');this.elements.select2.off('dblclick');}if (refresh) {refreshSelects(this);}return this.element;},setPreserveSelectionOnMove: function(value, refresh) {// We are forcing to move on select and disabling preserveSelectionOnMove on Androidif (isBuggyAndroid) {value = false;}this.settings.preserveSelectionOnMove = value;if (refresh) {refreshSelects(this);}return this.element;},setSelectedListLabel: function(value, refresh) {this.settings.selectedListLabel = value;if (value) {this.elements.label2.show().html(value);} else {this.elements.label2.hide().html(value);}if (refresh) {refreshSelects(this);}return this.element;},setNonSelectedListLabel: function(value, refresh) {this.settings.nonSelectedListLabel = value;if (value) {this.elements.label1.show().html(value);} else {this.elements.label1.hide().html(value);}if (refresh) {refreshSelects(this);}return this.element;},setHelperSelectNamePostfix: function(value, refresh) {this.settings.helperSelectNamePostfix = value;if (value) {this.elements.select1.attr('name', this.originalSelectName + value + '1');this.elements.select2.attr('name', this.originalSelectName + value + '2');} else {this.elements.select1.removeAttr('name');this.elements.select2.removeAttr('name');}if (refresh) {refreshSelects(this);}return this.element;},setSelectOrMinimalHeight: function(value, refresh) {this.settings.selectorMinimalHeight = value;var height = this.element.height();if (this.element.height() < value) {height = value;}this.elements.select1.height(height);this.elements.select2.height(height);if (refresh) {refreshSelects(this);}return this.element;},setShowFilterInputs: function(value, refresh) {if (!value) {this.setNonSelectedFilter('');this.setSelectedFilter('');refreshSelects(this);this.elements.filterInput1.hide();this.elements.filterInput2.hide();} else {this.elements.filterInput1.show();this.elements.filterInput2.show();}this.settings.showFilterInputs = value;if (refresh) {refreshSelects(this);}return this.element;},setNonSelectedFilter: function(value, refresh) {if (this.settings.showFilterInputs) {this.settings.nonSelectedFilter = value;this.elements.filterInput1.val(value);if (refresh) {refreshSelects(this);}return this.element;}},setSelectedFilter: function(value, refresh) {if (this.settings.showFilterInputs) {this.settings.selectedFilter = value;this.elements.filterInput2.val(value);if (refresh) {refreshSelects(this);}return this.element;}},setInfoText: function(value, refresh) {this.settings.infoText = value;if (value) {this.elements.info1.show();this.elements.info2.show();} else {this.elements.info1.hide();this.elements.info2.hide();}if (refresh) {refreshSelects(this);}return this.element;},setInfoTextFiltered: function(value, refresh) {this.settings.infoTextFiltered = value;if (refresh) {refreshSelects(this);}return this.element;},setInfoTextEmpty: function(value, refresh) {this.settings.infoTextEmpty = value;if (refresh) {refreshSelects(this);}return this.element;},setFilterOnValues: function(value, refresh) {this.settings.filterOnValues = value;if (refresh) {refreshSelects(this);}return this.element;},setSortByInputOrder: function(value, refresh){this.settings.sortByInputOrder = value;if (refresh) {refreshSelects(this);}return this.element;},setEventMoveOverride: function(value, refresh) {this.settings.eventMoveOverride = value;if (refresh) {refreshSelects(this);}return this.element;},setEventMoveAllOverride: function(value, refresh) {this.settings.eventMoveAllOverride = value;if (refresh) {refreshSelects(this);}return this.element;},setEventRemoveOverride: function(value, refresh) {this.settings.eventRemoveOverride = value;if (refresh) {refreshSelects(this);}return this.element;},setEventRemoveAllOverride: function(value, refresh) {this.settings.eventRemoveAllOverride = value;if (refresh) {refreshSelects(this);}return this.element;},getContainer: function() {return this.container;},refresh: function(mustClearSelections) {updateSelectionStates(this);if (!mustClearSelections) {saveSelections(this, 1);saveSelections(this, 2);} else {clearSelections(this);}refreshSelects(this);},destroy: function() {this.container.remove();this.element.show();$.data(this, 'plugin_' + pluginName, null);return this.element;}};// A really lightweight plugin wrapper around the constructor,// preventing against multiple instantiations$.fn[ pluginName ] = function (options) {var args = arguments;// Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin.if (options === undefined || typeof options === 'object') {return this.each(function () {// If this is not a selectif (!$(this).is('select')) {$(this).find('select').each(function(index, item) {// For each nested select, instantiate the Dual List Box$(item).bootstrapDualListbox(options);});} else if (!$.data(this, 'plugin_' + pluginName)) {// Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet// if it has no instance, create a new one, pass options to our plugin constructor,// and store the plugin instance in the elements jQuery data object.$.data(this, 'plugin_' + pluginName, new BootstrapDualListbox(this, options));}});// If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function,// treat this as a call to a public method.} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {// Cache the method call to make it possible to return a valuevar returns;this.each(function () {var instance = $.data(this, 'plugin_' + pluginName);// Tests that there's already a plugin-instance and checks that the requested public method existsif (instance instanceof BootstrapDualListbox && typeof instance[options] === 'function') {// Call the method of our plugin instance, and pass it the supplied arguments.returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));}});// If the earlier cached method gives a value back return the value,// otherwise return this to preserve chainability.return returns !== undefined ? returns : this;}};})(jQuery, window, document);