| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/) |
| 3 | 3 | * |
| 4 | 4 | * Apache License, Version 2.0: |
| 5 | - * Copyright (c) 2012 - 2022 David Stutz | |
| 5 | + * Copyright (c) 2012 - 2021 David Stutz | |
| 6 | 6 | * |
| 7 | 7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 8 | 8 | * use this file except in compliance with the License. You may obtain a |
| 9 | 9 | * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| @@ -14,9 +14,9 @@ | ||
| 14 | 14 | * License for the specific language governing permissions and limitations |
| 15 | 15 | * under the License. |
| 16 | 16 | * |
| 17 | 17 | * BSD 3-Clause License: |
| 18 | - * Copyright (c) 2012 - 2022 David Stutz | |
| 18 | + * Copyright (c) 2012 - 2021 David Stutz | |
| 19 | 19 | * All rights reserved. |
| 20 | 20 | * |
| 21 | 21 | * Redistribution and use in source and binary forms, with or without |
| 22 | 22 | * modification, are permitted provided that the following conditions are met: |
| @@ -40,5 +40,1976 @@ | ||
| 40 | 40 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 41 | 41 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 42 | 42 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 43 | 43 | */ |
| 44 | -!function(t,e){"function"==typeof define&&define.amd&&"function"==typeof require&&"function"==typeof require.specified&&require.specified("knockout")?define(["jquery","knockout"],e):e(t.jQuery,t.ko)}(this,(function(t,e){"use strict";void 0!==e&&e.bindingHandlers&&!e.bindingHandlers.multiselect&&(e.bindingHandlers.multiselect={after:["options","value","selectedOptions","enable","disable"],init:function(i,s,o,n,l){var p=t(i),a=e.toJS(s());if(p.multiselect(a),o.has("options")){var r=o.get("options");e.isObservable(r)&&e.computed({read:function(){r(),setTimeout((function(){var t=p.data("multiselect");t&&t.updateOriginalOptions(),p.multiselect("rebuild")}),1)},disposeWhenNodeIsRemoved:i})}if(o.has("value")){var c=o.get("value");e.isObservable(c)&&e.computed({read:function(){c(),setTimeout((function(){p.multiselect("refresh")}),1)},disposeWhenNodeIsRemoved:i}).extend({rateLimit:100,notifyWhenChangesStop:!0})}if(o.has("selectedOptions")){var h=o.get("selectedOptions");e.isObservable(h)&&e.computed({read:function(){h(),setTimeout((function(){p.multiselect("refresh")}),1)},disposeWhenNodeIsRemoved:i}).extend({rateLimit:100,notifyWhenChangesStop:!0})}var u=function(t){setTimeout((function(){t?p.multiselect("enable"):p.multiselect("disable")}))};if(o.has("enable")){var d=o.get("enable");e.isObservable(d)?e.computed({read:function(){u(d())},disposeWhenNodeIsRemoved:i}).extend({rateLimit:100,notifyWhenChangesStop:!0}):u(d)}if(o.has("disable")){var f=o.get("disable");e.isObservable(f)?e.computed({read:function(){u(!f())},disposeWhenNodeIsRemoved:i}).extend({rateLimit:100,notifyWhenChangesStop:!0}):u(!f)}e.utils.domNodeDisposal.addDisposeCallback(i,(function(){p.multiselect("destroy")}))},update:function(i,s,o,n,l){var p=t(i),a=e.toJS(s());p.multiselect("setOptions",a),p.multiselect("rebuild")}});function i(e,i){this.$select=t(e),this.options=this.mergeOptions(t.extend({},i,this.$select.data())),this.$select.attr("data-placeholder")&&(this.options.nonSelectedText=this.$select.data("placeholder")),this.originalOptions=this.$select.clone()[0].options,this.query="",this.searchTimeout=null,this.lastToggledInput=null,this.multiselectId=this.generateUniqueId()+"_0",this.internalIdCount=0,this.options.multiple="multiple"===this.$select.attr("multiple"),this.options.onChange=t.proxy(this.options.onChange,this),this.options.onSelectAll=t.proxy(this.options.onSelectAll,this),this.options.onDeselectAll=t.proxy(this.options.onDeselectAll,this),this.options.onDropdownShow=t.proxy(this.options.onDropdownShow,this),this.options.onDropdownHide=t.proxy(this.options.onDropdownHide,this),this.options.onDropdownShown=t.proxy(this.options.onDropdownShown,this),this.options.onDropdownHidden=t.proxy(this.options.onDropdownHidden,this),this.options.onInitialized=t.proxy(this.options.onInitialized,this),this.options.onFiltering=t.proxy(this.options.onFiltering,this),this.buildContainer(),this.buildButton(),this.buildDropdown(),this.buildReset(),this.buildSelectAll(),this.buildDropdownOptions(),this.buildFilter(),this.buildButtons(),this.updateButtonText(),this.updateSelectAll(!0),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups(),this.options.wasDisabled=this.$select.prop("disabled"),this.options.disableIfEmpty&&t("option",this.$select).length<=0&&!this.options.wasDisabled&&this.disable(!0),this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container),this.$select.prop("tabindex","-1"),"never"!==this.options.widthSynchronizationMode&&this.synchronizeButtonAndPopupWidth(),this.$select.data("multiselect",this),this.options.onInitialized(this.$select,this.$container)}i.prototype={defaults:{buttonText:function(e,i){if(this.disabledText.length>0&&i.prop("disabled"))return this.disabledText;if(0===e.length)return this.nonSelectedText;if(this.allSelectedText&&e.length===t("option",t(i)).length&&1!==t("option",t(i)).length&&this.multiple)return this.selectAllNumber?this.allSelectedText+" ("+e.length+")":this.allSelectedText;if(0!=this.numberDisplayed&&e.length>this.numberDisplayed)return e.length+" "+this.nSelectedText;var s="",o=this.delimiterText;return e.each((function(){var e=void 0!==t(this).attr("label")?t(this).attr("label"):t(this).text();s+=e+o})),s.substr(0,s.length-this.delimiterText.length)},buttonTitle:function(e,i){if(0===e.length)return this.nonSelectedText;var s="",o=this.delimiterText;return e.each((function(){var e=void 0!==t(this).attr("label")?t(this).attr("label"):t(this).text();s+=e+o})),s.substr(0,s.length-this.delimiterText.length)},checkboxName:function(t){return!1},optionLabel:function(e){return t(e).attr("label")||t(e).text()},optionClass:function(e){return t(e).attr("class")||""},onChange:function(t,e){},onDropdownShow:function(t){},onDropdownHide:function(t){},onDropdownShown:function(t){},onDropdownHidden:function(t){},onSelectAll:function(t){},onDeselectAll:function(t){},onInitialized:function(t,e){},onFiltering:function(t){},enableHTML:!1,buttonClass:"custom-select",inheritClass:!1,buttonWidth:"auto",buttonContainer:'<div class="btn-group" />',dropRight:!1,dropUp:!1,selectedClass:"active",maxHeight:null,includeSelectAllOption:!1,includeSelectAllIfMoreThan:0,selectAllText:" Select all",selectAllValue:"multiselect-all",selectAllName:!1,selectAllNumber:!0,selectAllJustVisible:!0,enableFiltering:!1,enableCaseInsensitiveFiltering:!1,enableFullValueFiltering:!1,enableClickableOptGroups:!1,enableCollapsibleOptGroups:!1,collapseOptGroupsByDefault:!1,filterPlaceholder:"Search",filterBehavior:"text",includeFilterClearBtn:!0,preventInputChangeEvent:!1,nonSelectedText:"None selected",nSelectedText:"selected",allSelectedText:"All selected",resetButtonText:"Reset",numberDisplayed:3,disableIfEmpty:!1,disabledText:"",delimiterText:", ",includeResetOption:!1,includeResetDivider:!1,resetText:"Reset",indentGroupOptions:!0,widthSynchronizationMode:"never",buttonTextAlignment:"center",enableResetButton:!1,templates:{button:'<button type="button" class="multiselect form-select dropdown-toggle" data-bs-toggle="dropdown"><span class="multiselect-selected-text"></span></button>',popupContainer:'<div class="multiselect-container dropdown-menu"></div>',filter:'<div class="multiselect-filter d-flex align-items-center"><i class="fas fa-sm fa-search text-body-secondary"></i><input type="search" class="multiselect-search form-control" /></div>',buttonGroup:'<div class="multiselect-buttons btn-group" style="display:flex;"></div>',buttonGroupReset:'<button type="button" class="multiselect-reset btn btn-secondary w-100"></button>',option:'<button type="button" class="multiselect-option dropdown-item"></button>',divider:'<div class="dropdown-divider"></div>',optionGroup:'<button type="button" class="multiselect-group dropdown-item"></button>',resetButton:'<div class="multiselect-reset text-center p-2"><button type="button" class="btn btn-sm w-100 btn-outline-secondary"></button></div>'}},constructor:i,buildContainer:function(){this.$container=t(this.options.buttonContainer),"never"!==this.options.widthSynchronizationMode?this.$container.on("show.bs.dropdown",t.proxy((function(){this.synchronizeButtonAndPopupWidth(),this.options.onDropdownShow()}),this)):this.$container.on("show.bs.dropdown",this.options.onDropdownShow),this.$container.on("hide.bs.dropdown",this.options.onDropdownHide),this.$container.on("shown.bs.dropdown",this.options.onDropdownShown),this.$container.on("hidden.bs.dropdown",this.options.onDropdownHidden)},buildButton:function(){if(this.$button=t(this.options.templates.button).addClass(this.options.buttonClass),this.$select.attr("class")&&this.options.inheritClass&&this.$button.addClass(this.$select.attr("class")),this.$select.prop("disabled")?this.disable():this.enable(),this.options.buttonWidth&&"auto"!==this.options.buttonWidth&&(this.$button.css({width:"100%"}),this.$container.css({width:this.options.buttonWidth})),this.options.buttonTextAlignment)switch(this.options.buttonTextAlignment){case"left":this.$button.addClass("text-start");break;case"center":this.$button.addClass("text-center");break;case"right":this.$button.addClass("text-end")}var e=this.$select.attr("tabindex");e&&this.$button.attr("tabindex",e),this.$container.prepend(this.$button)},buildDropdown:function(){this.$popupContainer=t(this.options.templates.popupContainer),this.options.dropRight?this.$container.addClass("dropend"):this.options.dropUp&&this.$container.addClass("dropup"),this.options.maxHeight&&this.$popupContainer.css({"max-height":this.options.maxHeight+"px","overflow-y":"auto","overflow-x":"hidden"}),"never"!==this.options.widthSynchronizationMode&&this.$popupContainer.css("overflow-x","hidden"),this.$popupContainer.on("touchstart click",(function(t){t.stopPropagation()})),this.$container.append(this.$popupContainer)},synchronizeButtonAndPopupWidth:function(){if(this.$popupContainer&&"never"!==this.options.widthSynchronizationMode){var t=this.$button.outerWidth();switch(this.options.widthSynchronizationMode){case"always":this.$popupContainer.css("min-width",t),this.$popupContainer.css("max-width",t);break;case"ifPopupIsSmaller":this.$popupContainer.css("min-width",t);break;case"ifPopupIsWider":this.$popupContainer.css("max-width",t)}}},buildDropdownOptions:function(){if(this.$select.children().each(t.proxy((function(e,i){var s=t(i),o=s.prop("tagName").toLowerCase();s.prop("value")!==this.options.selectAllValue&&("optgroup"===o?this.createOptgroup(i):"option"===o&&("divider"===s.data("role")?this.createDivider():this.createOptionValue(i,!1)))}),this)),t(this.$popupContainer).off("change",'> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]'),t(this.$popupContainer).on("change",'> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]',t.proxy((function(e){var i=t(e.target),s=i.prop("checked")||!1,o=i.val()===this.options.selectAllValue;this.options.selectedClass&&(s?i.closest(".multiselect-option").addClass(this.options.selectedClass):i.closest(".multiselect-option").removeClass(this.options.selectedClass));var n=i.attr("id"),l=this.getOptionById(n),p=t("option",this.$select).not(l),a=t("input",this.$container).not(i);if(o?s?this.selectAll(this.options.selectAllJustVisible,!0):this.deselectAll(this.options.selectAllJustVisible,!0):(s?(l.prop("selected",!0),this.options.multiple?l.prop("selected",!0):(this.options.selectedClass&&t(a).closest(".dropdown-item").removeClass(this.options.selectedClass),t(a).prop("checked",!1),p.prop("selected",!1),this.$button.click()),"active"===this.options.selectedClass&&p.closest(".dropdown-item").css("outline","")):l.prop("selected",!1),this.options.onChange(l,s),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()),this.$select.change(),this.updateButtonText(),this.options.preventInputChangeEvent)return!1}),this)),t(".multiselect-option",this.$popupContainer).off("mousedown"),t(".multiselect-option",this.$popupContainer).on("mousedown",(function(t){if(t.shiftKey)return!1})),t(this.$popupContainer).off("touchstart click",".multiselect-option, .multiselect-all, .multiselect-group"),t(this.$popupContainer).on("touchstart click",".multiselect-option, .multiselect-all, .multiselect-group",t.proxy((function(e){e.stopPropagation();var i=t(e.target);if(e.shiftKey&&this.options.multiple){i.is("input")||(e.preventDefault(),(i=i.closest(".multiselect-option").find("input")).prop("checked",!i.prop("checked")));var s=i.prop("checked")||!1;if(null!==this.lastToggledInput&&this.lastToggledInput!==i){var o=this.$popupContainer.find(".multiselect-option:visible").index(i.closest(".multiselect-option")),n=this.$popupContainer.find(".multiselect-option:visible").index(this.lastToggledInput.closest(".multiselect-option"));if(o>n){var l=n;n=o,o=l}++n;var p=this.$popupContainer.find(".multiselect-option:not(.multiselect-filter-hidden)").slice(o,n).find("input");p.prop("checked",s),this.options.selectedClass&&p.closest(".multiselect-option").toggleClass(this.options.selectedClass,s);for(var a=0,r=p.length;a<r;a++){var c=t(p[a]);this.getOptionById(c.attr("id")).prop("selected",s)}}i.trigger("change")}else if(!i.is("input")){if((c=i.closest(".multiselect-option, .multiselect-all").find(".form-check-input")).length>0)!this.options.multiple&&c.prop("checked")||(c.prop("checked",!c.prop("checked")),c.change());else if(this.options.enableClickableOptGroups&&this.options.multiple&&!i.hasClass("caret-container")){var h=i;h.hasClass("multiselect-group")||(h=i.closest(".multiselect-group")),(c=h.find(".form-check-input")).length>0&&(c.prop("checked",!c.prop("checked")),c.change())}e.preventDefault()}i.closest(".multiselect-option").find("input[type='checkbox'], input[type='radio']").length>0?this.lastToggledInput=i:this.lastToggledInput=null,i.blur()}),this)),this.$container.off("keydown.multiselect").on("keydown.multiselect",t.proxy((function(e){var i=t(this.$container).find(".multiselect-option:not(.disabled), .multiselect-group:not(.disabled), .multiselect-all").filter(":visible"),s=i.index(i.filter(":focus")),o=t(".multiselect-search",this.$container);if(9===e.keyCode&&this.$container.hasClass("show"))this.$button.click();else if(13==e.keyCode){var n=i.eq(s);setTimeout((function(){n.focus()}),1)}else if(38==e.keyCode)0!=s||o.is(":focus")||setTimeout((function(){o.focus()}),1);else if(40==e.keyCode)if(o.is(":focus")){var l=i.eq(0);setTimeout((function(){o.blur(),l.focus()}),1)}else-1==s&&setTimeout((function(){o.focus()}),1)}),this)),this.options.enableClickableOptGroups&&this.options.multiple&&(t(".multiselect-group input",this.$popupContainer).off("change"),t(".multiselect-group input",this.$popupContainer).on("change",t.proxy((function(e){e.stopPropagation();var i=t(e.target).prop("checked")||!1,s=t(e.target).closest(".dropdown-item"),o=s.nextUntil(".multiselect-group").not(".multiselect-filter-hidden").not(".disabled").find("input"),n=[];this.options.selectedClass&&(i?s.addClass(this.options.selectedClass):s.removeClass(this.options.selectedClass)),t.each(o,t.proxy((function(e,s){var o=t(s),l=o.attr("id"),p=this.getOptionById(l);i?(o.prop("checked",!0),o.closest(".dropdown-item").addClass(this.options.selectedClass),p.prop("selected",!0)):(o.prop("checked",!1),o.closest(".dropdown-item").removeClass(this.options.selectedClass),p.prop("selected",!1)),n.push(p)}),this)),this.options.onChange(n,i),this.$select.change(),this.updateButtonText(),this.updateSelectAll()}),this))),this.options.enableCollapsibleOptGroups){let e=this.options.enableClickableOptGroups?".multiselect-group .caret-container":".multiselect-group";t(e,this.$popupContainer).off("click"),t(e,this.$popupContainer).on("click",t.proxy((function(e){var i=t(e.target).closest(".multiselect-group"),s=i.nextUntil(".multiselect-group").not(".multiselect-filter-hidden"),o=!0;s.each((function(){o=o&&!t(this).hasClass("multiselect-collapsible-hidden")})),o?(s.hide().addClass("multiselect-collapsible-hidden"),i.get(0).classList.add("closed")):(s.show().removeClass("multiselect-collapsible-hidden"),i.get(0).classList.remove("closed"))}),this))}},createCheckbox:function(e,i,s,o,n,l,p){var a=t("<span />");a.addClass("form-check");var r=t('<label class="form-check-label" />');this.options.enableHTML&&t(i).length>0?r.html(i):r.text(i),a.append(r);var c=t('<input class="form-check-input"/>').attr("type",l);return c.val(o),a.prepend(c),p&&(c.attr("id",p),r.attr("for",p)),s&&c.attr("name",s),e.prepend(a),e.attr("title",n||i),c},createOptionValue:function(e,i){var s=t(e);s.is(":selected")&&s.prop("selected",!0);var o=this.options.optionLabel(e),n=this.options.optionClass(e),l=s.val(),p=this.options.multiple?"checkbox":"radio",a=s.attr("title"),r=t(this.options.templates.option);r.addClass(n),i&&this.options.indentGroupOptions&&(this.options.enableCollapsibleOptGroups?r.addClass("multiselect-group-option-indented-full"):r.addClass("multiselect-group-option-indented")),this.options.collapseOptGroupsByDefault&&"optgroup"===t(e).parent().prop("tagName").toLowerCase()&&(r.addClass("multiselect-collapsible-hidden"),r.hide());var c=this.options.checkboxName(s),h=this.createAndApplyUniqueId(s),u=this.createCheckbox(r,o,c,l,a,p,h),d=s.prop("selected")||!1;l===this.options.selectAllValue&&(r.addClass("multiselect-all"),r.removeClass("multiselect-option"),u.parent().parent().addClass("multiselect-all")),this.$popupContainer.append(r),s.is(":disabled")&&u.attr("disabled","disabled").prop("disabled",!0).closest(".dropdown-item").addClass("disabled"),u.prop("checked",d),d&&this.options.selectedClass&&u.closest(".dropdown-item").addClass(this.options.selectedClass)},createDivider:function(e){var i=t(this.options.templates.divider);this.$popupContainer.append(i)},createOptgroup:function(e){var i=t(e),s=i.attr("label"),o=i.attr("value"),n=i.attr("title"),l=t("<span class='multiselect-group dropdown-item-text'></span>");if(this.options.enableClickableOptGroups&&this.options.multiple){l=t(this.options.templates.optionGroup);var p=this.createAndApplyUniqueId(i);this.createCheckbox(l,s,null,o,n,"checkbox",p)}else this.options.enableHTML?l.html(" "+s):l.text(" "+s);var a=this.options.optionClass(e);l.addClass(a),this.options.enableCollapsibleOptGroups&&(l.find(".form-check").addClass("d-inline-block"),l.get(0).insertAdjacentHTML("afterbegin",'<span class="caret-container dropdown-toggle"></span>')),i.is(":disabled")&&l.addClass("disabled"),this.$popupContainer.append(l),t("option",e).each(t.proxy((function(t,e){this.createOptionValue(e,!0)}),this))},buildReset:function(){if(this.options.includeResetOption){if(this.options.includeResetDivider){var e=t(this.options.templates.divider);e.addClass("mt-0"),this.$popupContainer.prepend(e)}var i=t(this.options.templates.resetButton);this.options.enableHTML?t("button",i).html(this.options.resetText):t("button",i).text(this.options.resetText),t("button",i).click(t.proxy((function(){this.clearSelection()}),this)),this.$popupContainer.prepend(i)}},buildSelectAll:function(){if("number"==typeof this.options.selectAllValue&&(this.options.selectAllValue=this.options.selectAllValue.toString()),!this.hasSelectAll()&&this.options.includeSelectAllOption&&this.options.multiple&&t("option",this.$select).length>this.options.includeSelectAllIfMoreThan){this.options.includeSelectAllDivider&&this.$popupContainer.prepend(t(this.options.templates.divider));var e=t(this.options.templates.li||this.options.templates.option),i=this.createCheckbox(e,this.options.selectAllText,this.options.selectAllName,this.options.selectAllValue,this.options.selectAllText,"checkbox",this.createAndApplyUniqueId(null));e.addClass("multiselect-all"),e.removeClass("multiselect-option"),e.find(".form-check-label").addClass("font-weight-bold"),this.$popupContainer.prepend(e),i.prop("checked",!1)}},buildFilter:function(){if(this.options.enableFiltering||this.options.enableCaseInsensitiveFiltering){var e=Math.max(this.options.enableFiltering,this.options.enableCaseInsensitiveFiltering);this.$select.find("option").length>=e&&(this.$filter=t(this.options.templates.filter),t("input",this.$filter).attr("placeholder",this.options.filterPlaceholder),this.options.includeFilterClearBtn?(this.isFirefox()&&0===this.$filter.find(".multiselect-clear-filter").length&&this.$filter.append("<i class='fas fa-times text-body-secondary multiselect-clear-filter multiselect-moz-clear-filter'></i>"),this.$filter.find(".multiselect-clear-filter").on("click",t.proxy((function(e){clearTimeout(this.searchTimeout),this.query="",this.$filter.find(".multiselect-search").val(""),t(".dropdown-item",this.$popupContainer).show().removeClass("multiselect-filter-hidden"),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()}),this))):(this.$filter.find(".multiselect-search").attr("type","text"),this.$filter.find(".multiselect-clear-filter").remove()),this.$popupContainer.prepend(this.$filter),this.$filter.val(this.query).on("click",(function(t){t.stopPropagation()})).on("input keydown",t.proxy((function(e){13===e.which&&e.preventDefault(),this.isFirefox()&&this.options.includeFilterClearBtn&&(e.target.value?this.$filter.find(".multiselect-moz-clear-filter").show():this.$filter.find(".multiselect-moz-clear-filter").hide()),clearTimeout(this.searchTimeout),this.searchTimeout=this.asyncFunction(t.proxy((function(){var i,s;this.query!==e.target.value&&(this.query=e.target.value,t.each(t(".multiselect-option, .multiselect-group",this.$popupContainer),t.proxy((function(e,o){var n=t("input",o).length>0?t("input",o).val():"",l=t(".form-check-label",o).text(),p="";if("text"===this.options.filterBehavior?p=l:"value"===this.options.filterBehavior?p=n:"both"===this.options.filterBehavior&&(p=l+"\n"+n),n!==this.options.selectAllValue&&l){var a=!1;if(this.options.enableCaseInsensitiveFiltering&&(p=p.toLowerCase(),this.query=this.query.toLowerCase()),this.options.enableFullValueFiltering&&"both"!==this.options.filterBehavior){var r=p.trim().substring(0,this.query.length);this.query.indexOf(r)>-1&&(a=!0)}else p.indexOf(this.query)>-1&&(a=!0);a||(t(o).css("display","none"),t(o).addClass("multiselect-filter-hidden")),a&&(t(o).css("display","block"),t(o).removeClass("multiselect-filter-hidden")),t(o).hasClass("multiselect-group")?(i=o,s=a):(a&&t(i).show().removeClass("multiselect-filter-hidden"),!a&&s&&t(o).show().removeClass("multiselect-filter-hidden"))}}),this)));this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups(),this.updatePopupPosition(),this.options.onFiltering(e.target)}),this),300,this)}),this)))}},buildButtons:function(){if(this.options.enableResetButton){var e=t(this.options.templates.buttonGroup);this.$buttonGroupReset=t(this.options.templates.buttonGroupReset).text(this.options.resetButtonText),e.append(this.$buttonGroupReset),this.$popupContainer.prepend(e),this.defaultSelection={},t("option",this.$select).each(t.proxy((function(e,i){var s=t(i);this.defaultSelection[s.val()]=s.prop("selected")}),this)),this.$buttonGroupReset.on("click",t.proxy((function(e){t("option",this.$select).each(t.proxy((function(e,i){var s=t(i);s.prop("selected",this.defaultSelection[s.val()])}),this)),this.refresh(),this.options.enableFiltering&&(this.$filter.trigger("keydown"),t("input",this.$filter).val(""))}),this))}},updatePopupPosition:function(){var t=this.$popupContainer.css("transform"),e=t.substring(0,t.indexOf("(")),i=t.substring(t.indexOf("(")+1,t.length-1).split(","),s=5;if("matrix3d"===e&&(s=13),!(i.length<s)){var o=i[s];(o=void 0===o?0:o.trim())<0&&(o=-1*this.$popupContainer.css("height").replace("px",""),i[s]=o,t=e+"("+i.join(",")+")",this.$popupContainer.css("transform",t))}},destroy:function(){this.$container.remove(),this.$select.unwrap(),this.$select.show(),this.$select.prop("disabled",this.options.wasDisabled),this.$select.find("option, optgroup").removeAttr("data-multiselectid"),this.$select.data("multiselect",null)},refresh:function(){var e={};t(".multiselect-option input",this.$popupContainer).each((function(){e[t(this).val()]=t(this)})),t("option",this.$select).each(t.proxy((function(i,s){var o=t(s),n=e[t(s).val()];o.is(":selected")?(n.prop("checked",!0),this.options.selectedClass&&n.closest(".multiselect-option").addClass(this.options.selectedClass)):(n.prop("checked",!1),this.options.selectedClass&&n.closest(".multiselect-option").removeClass(this.options.selectedClass)),o.is(":disabled")?n.attr("disabled","disabled").prop("disabled",!0).closest(".multiselect-option").addClass("disabled"):n.prop("disabled",!1).closest(".multiselect-option").removeClass("disabled")}),this)),this.updateButtonText(),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()},select:function(e,i){t.isArray(e)||(e=[e]);for(var s=0;s<e.length;s++){var o=e[s];if(null!=o){var n=this.getInputsByValue(o);if(n&&0!==n.length)for(var l=0;l<n.length;++l){var p=n[l],a=this.getOptionById(p.attr("id"));if(void 0!==a){if(this.options.selectedClass&&p.closest(".dropdown-item").addClass(this.options.selectedClass),p.prop("checked",!0),a.prop("selected",!0),!this.options.multiple){var r=t("input",this.$container).not(p);t(r).prop("checked",!1),t(r).closest(".multiselect-option").removeClass("active"),t("option",this.$select).not(a).prop("selected",!1)}i&&this.options.onChange(a,!0)}}}}this.updateButtonText(),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()},clearSelection:function(){this.deselectAll(!1),this.updateButtonText(),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()},deselect:function(e,i){if(this.options.multiple){t.isArray(e)||(e=[e]);for(var s=0;s<e.length;s++){var o=e[s];if(null!=o){var n=this.getInputsByValue(o);if(n&&0!==n.length)for(var l=0;l<n.length;++l){var p=n[l],a=this.getOptionById(p.attr("id"));a&&(this.options.selectedClass&&p.closest(".dropdown-item").removeClass(this.options.selectedClass),p.prop("checked",!1),a.prop("selected",!1),i&&this.options.onChange(a,!1))}}}this.updateButtonText(),this.updateSelectAll(),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups()}},selectAll:function(e,i){if(this.options.multiple){var s=[];if(e=void 0===e||e){var o=t(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)",this.$popupContainer);t("input:enabled",o).prop("checked",!0),o.addClass(this.options.selectedClass),t("input:enabled",o).each(t.proxy((function(e,i){var o=t(i).attr("id"),n=this.getOptionById(o);t(n).prop("selected")||s.push(n),t(n).prop("selected",!0)}),this))}else{var n=t(".multiselect-option:not(.disabled)",this.$popupContainer);t("input:enabled",n).prop("checked",!0),n.addClass(this.options.selectedClass),t("input:enabled",n).each(t.proxy((function(e,i){var o=t(i).attr("id"),n=this.getOptionById(o);t(n).prop("selected")||s.push(n),t(n).prop("selected",!0)}),this))}t('.multiselect-option input[value="'+this.options.selectAllValue+'"]',this.$popupContainer).prop("checked",!0),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups(),this.updateButtonText(),this.updateSelectAll(),i&&this.options.onSelectAll(s)}},deselectAll:function(e,i){if(this.options.multiple){var s=[];if(e=void 0===e||e){var o=t(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)",this.$popupContainer);t('input[type="checkbox"]:enabled',o).prop("checked",!1),o.removeClass(this.options.selectedClass),t('input[type="checkbox"]:enabled',o).each(t.proxy((function(e,i){var o=t(i).attr("id"),n=this.getOptionById(o);t(n).prop("selected")&&s.push(n),t(n).prop("selected",!1)}),this))}else{var n=t(".multiselect-option:not(.disabled):not(.multiselect-group)",this.$popupContainer);t('input[type="checkbox"]:enabled',n).prop("checked",!1),n.removeClass(this.options.selectedClass),t('input[type="checkbox"]:enabled',n).each(t.proxy((function(e,i){var o=t(i).attr("id"),n=this.getOptionById(o);t(n).prop("selected")&&s.push(n),t(n).prop("selected",!1)}),this))}t('.multiselect-all input[value="'+this.options.selectAllValue+'"]',this.$popupContainer).prop("checked",!1),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups(),this.updateButtonText(),this.updateSelectAll(),i&&this.options.onDeselectAll(s)}},rebuild:function(){this.internalIdCount=0,this.$popupContainer.html(""),this.$select.find("option, optgroup").removeAttr("data-multiselectid"),this.options.multiple="multiple"===this.$select.attr("multiple"),this.buildSelectAll(),this.buildDropdownOptions(),this.buildFilter(),this.buildButtons(),this.updateButtonText(),this.updateSelectAll(!0),this.options.enableClickableOptGroups&&this.options.multiple&&this.updateOptGroups(),this.options.disableIfEmpty&&(t("option",this.$select).length<=0?this.$select.prop("disabled")||this.disable(!0):this.$select.data("disabled-by-option")&&this.enable()),this.options.dropRight?this.$container.addClass("dropend"):this.options.dropUp&&this.$container.addClass("dropup"),"never"!==this.options.widthSynchronizationMode&&this.synchronizeButtonAndPopupWidth()},dataprovider:function(e){var i=0,s=this.$select.empty();t.each(e,(function(e,o){var n;if(t.isArray(o.children))i++,n=t("<optgroup/>").attr({label:o.label||"Group "+i,disabled:!!o.disabled,value:o.value}),function(t,e){for(var i=0;i<t.length;++i)e(t[i],i)}(o.children,(function(e){var i={value:e.value,label:void 0!==e.label&&null!==e.label?e.label:e.value,title:e.title,class:e.class,selected:!!e.selected,disabled:!!e.disabled};for(var s in e.attributes)i["data-"+s]=e.attributes[s];n.append(t("<option/>").attr(i))}));else{var l={value:o.value,label:void 0!==o.label&&null!==o.label?o.label:o.value,title:o.title,class:o.class,selected:!!o.selected,disabled:!!o.disabled};for(var p in o.attributes)l["data-"+p]=o.attributes[p];(n=t("<option/>").attr(l)).text(void 0!==o.label&&null!==o.label?o.label:o.value)}s.append(n)})),this.rebuild()},enable:function(){this.$select.prop("disabled",!1),this.$button.prop("disabled",!1).removeClass("disabled"),this.updateButtonText()},disable:function(t){this.$select.prop("disabled",!0),this.$button.prop("disabled",!0).addClass("disabled"),t?this.$select.data("disabled-by-option",!0):this.$select.data("disabled-by-option",null),this.updateButtonText()},setOptions:function(t){this.options=this.mergeOptions(t)},mergeOptions:function(e){return t.extend(!0,{},this.defaults,this.options,e)},hasSelectAll:function(){return t(".multiselect-all",this.$popupContainer).length>0},updateOptGroups:function(){var e=t(".multiselect-group",this.$popupContainer),i=this.options.selectedClass;e.each((function(){var e=t(this).nextUntil(".multiselect-group").not(".multiselect-filter-hidden").not(".disabled"),s=!0;e.each((function(){t("input",this).prop("checked")||(s=!1)})),i&&(s?t(this).addClass(i):t(this).removeClass(i)),t("input",this).prop("checked",s)}))},updateSelectAll:function(e){if(this.hasSelectAll()){var i=t(".multiselect-option:not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled",this.$popupContainer),s=i.length,o=i.filter(":checked").length,n=t(".multiselect-all",this.$popupContainer),l=n.find("input");o>0&&o===s?(l.prop("checked",!0),n.addClass(this.options.selectedClass)):(l.prop("checked",!1),n.removeClass(this.options.selectedClass))}},updateButtonText:function(){var e=this.getSelected();this.options.enableHTML?t(".multiselect .multiselect-selected-text",this.$container).html(this.options.buttonText(e,this.$select)):t(".multiselect .multiselect-selected-text",this.$container).text(this.options.buttonText(e,this.$select)),t(".multiselect",this.$container).attr("title",this.options.buttonTitle(e,this.$select)),this.$button.trigger("change")},getSelected:function(){return t("option",this.$select).filter(":selected")},getOptionById:function(t){return t?this.$select.find("option[data-multiselectid="+t+"], optgroup[data-multiselectid="+t+"]"):null},getInputsByValue:function(e){for(var i=t(".multiselect-option input:not(.multiselect-search)",this.$popupContainer),s=e.toString(),o=[],n=0;n<i.length;n+=1){var l=i[n];l.value===s&&o.push(t(l))}return o},updateOriginalOptions:function(){this.originalOptions=this.$select.clone()[0].options},asyncFunction:function(t,e,i){var s=Array.prototype.slice.call(arguments,3);return setTimeout((function(){t.apply(i||window,s)}),e)},setAllSelectedText:function(t){this.options.allSelectedText=t,this.updateButtonText()},isFirefox:function(){return!(!navigator||!navigator.userAgent)&&navigator.userAgent.toLocaleLowerCase().indexOf("firefox")>-1},createAndApplyUniqueId:function(t){var e="multiselect_"+this.multiselectId+"_"+this.internalIdCount++;return t&&(t[0].dataset.multiselectid=e),e},generateUniqueId:function(){return Math.random().toString(36).substr(2)}},t.fn.multiselect=function(e,s,o){return this.each((function(){var n=t(this).data("multiselect");n||(n=new i(this,"object"==typeof e&&e)),"string"==typeof e&&n[e](s,o)}))},t.fn.multiselect.Constructor=i,t((function(){t("select[data-role=multiselect]").multiselect()}))})); | |
| 44 | + (function (root, factory) { | |
| 45 | + // check to see if 'knockout' AMD module is specified if using requirejs | |
| 46 | + if (typeof define === 'function' && define.amd && | |
| 47 | + typeof require === 'function' && typeof require.specified === 'function' && require.specified('knockout')) { | |
| 48 | + | |
| 49 | + // AMD. Register as an anonymous module. | |
| 50 | + define(['jquery', 'knockout'], factory); | |
| 51 | + } else { | |
| 52 | + // Browser globals | |
| 53 | + factory(root.jQuery, root.ko); | |
| 54 | + } | |
| 55 | +})(this, function ($, ko) { | |
| 56 | + "use strict";// jshint ;_; | |
| 57 | + | |
| 58 | + if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) { | |
| 59 | + ko.bindingHandlers.multiselect = { | |
| 60 | + after: ['options', 'value', 'selectedOptions', 'enable', 'disable'], | |
| 61 | + | |
| 62 | + init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
| 63 | + var $element = $(element); | |
| 64 | + var config = ko.toJS(valueAccessor()); | |
| 65 | + | |
| 66 | + $element.multiselect(config); | |
| 67 | + | |
| 68 | + if (allBindings.has('options')) { | |
| 69 | + var options = allBindings.get('options'); | |
| 70 | + if (ko.isObservable(options)) { | |
| 71 | + ko.computed({ | |
| 72 | + read: function () { | |
| 73 | + options(); | |
| 74 | + setTimeout(function () { | |
| 75 | + var ms = $element.data('multiselect'); | |
| 76 | + if (ms) | |
| 77 | + ms.updateOriginalOptions();//Not sure how beneficial this is. | |
| 78 | + $element.multiselect('rebuild'); | |
| 79 | + }, 1); | |
| 80 | + }, | |
| 81 | + disposeWhenNodeIsRemoved: element | |
| 82 | + }); | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + //value and selectedOptions are two-way, so these will be triggered even by our own actions. | |
| 87 | + //It needs some way to tell if they are triggered because of us or because of outside change. | |
| 88 | + //It doesn't loop but it's a waste of processing. | |
| 89 | + if (allBindings.has('value')) { | |
| 90 | + var value = allBindings.get('value'); | |
| 91 | + if (ko.isObservable(value)) { | |
| 92 | + ko.computed({ | |
| 93 | + read: function () { | |
| 94 | + value(); | |
| 95 | + setTimeout(function () { | |
| 96 | + $element.multiselect('refresh'); | |
| 97 | + }, 1); | |
| 98 | + }, | |
| 99 | + disposeWhenNodeIsRemoved: element | |
| 100 | + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + //Switched from arrayChange subscription to general subscription using 'refresh'. | |
| 105 | + //Not sure performance is any better using 'select' and 'deselect'. | |
| 106 | + if (allBindings.has('selectedOptions')) { | |
| 107 | + var selectedOptions = allBindings.get('selectedOptions'); | |
| 108 | + if (ko.isObservable(selectedOptions)) { | |
| 109 | + ko.computed({ | |
| 110 | + read: function () { | |
| 111 | + selectedOptions(); | |
| 112 | + setTimeout(function () { | |
| 113 | + $element.multiselect('refresh'); | |
| 114 | + }, 1); | |
| 115 | + }, | |
| 116 | + disposeWhenNodeIsRemoved: element | |
| 117 | + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); | |
| 118 | + } | |
| 119 | + } | |
| 120 | + | |
| 121 | + var setEnabled = function (enable) { | |
| 122 | + setTimeout(function () { | |
| 123 | + if (enable) | |
| 124 | + $element.multiselect('enable'); | |
| 125 | + else | |
| 126 | + $element.multiselect('disable'); | |
| 127 | + }); | |
| 128 | + }; | |
| 129 | + | |
| 130 | + if (allBindings.has('enable')) { | |
| 131 | + var enable = allBindings.get('enable'); | |
| 132 | + if (ko.isObservable(enable)) { | |
| 133 | + ko.computed({ | |
| 134 | + read: function () { | |
| 135 | + setEnabled(enable()); | |
| 136 | + }, | |
| 137 | + disposeWhenNodeIsRemoved: element | |
| 138 | + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); | |
| 139 | + } else { | |
| 140 | + setEnabled(enable); | |
| 141 | + } | |
| 142 | + } | |
| 143 | + | |
| 144 | + if (allBindings.has('disable')) { | |
| 145 | + var disable = allBindings.get('disable'); | |
| 146 | + if (ko.isObservable(disable)) { | |
| 147 | + ko.computed({ | |
| 148 | + read: function () { | |
| 149 | + setEnabled(!disable()); | |
| 150 | + }, | |
| 151 | + disposeWhenNodeIsRemoved: element | |
| 152 | + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); | |
| 153 | + } else { | |
| 154 | + setEnabled(!disable); | |
| 155 | + } | |
| 156 | + } | |
| 157 | + | |
| 158 | + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { | |
| 159 | + $element.multiselect('destroy'); | |
| 160 | + }); | |
| 161 | + }, | |
| 162 | + | |
| 163 | + update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
| 164 | + var $element = $(element); | |
| 165 | + var config = ko.toJS(valueAccessor()); | |
| 166 | + | |
| 167 | + $element.multiselect('setOptions', config); | |
| 168 | + $element.multiselect('rebuild'); | |
| 169 | + } | |
| 170 | + }; | |
| 171 | + } | |
| 172 | + | |
| 173 | + function forEach(array, callback) { | |
| 174 | + for (var index = 0; index < array.length; ++index) { | |
| 175 | + callback(array[index], index); | |
| 176 | + } | |
| 177 | + } | |
| 178 | + | |
| 179 | + /** | |
| 180 | + * Constructor to create a new multiselect using the given select. | |
| 181 | + * | |
| 182 | + * @param {jQuery} select | |
| 183 | + * @param {Object} options | |
| 184 | + * @returns {Multiselect} | |
| 185 | + */ | |
| 186 | + function Multiselect(select, options) { | |
| 187 | + | |
| 188 | + this.$select = $(select); | |
| 189 | + this.options = this.mergeOptions($.extend({}, options, this.$select.data())); | |
| 190 | + | |
| 191 | + // Placeholder via data attributes | |
| 192 | + if (this.$select.attr("data-placeholder")) { | |
| 193 | + this.options.nonSelectedText = this.$select.data("placeholder"); | |
| 194 | + } | |
| 195 | + | |
| 196 | + // Initialization. | |
| 197 | + // We have to clone to create a new reference. | |
| 198 | + this.originalOptions = this.$select.clone()[0].options; | |
| 199 | + this.query = ''; | |
| 200 | + this.searchTimeout = null; | |
| 201 | + this.lastToggledInput = null; | |
| 202 | + | |
| 203 | + this.options.multiple = this.$select.attr('multiple') === "multiple"; | |
| 204 | + this.options.onChange = $.proxy(this.options.onChange, this); | |
| 205 | + this.options.onSelectAll = $.proxy(this.options.onSelectAll, this); | |
| 206 | + this.options.onDeselectAll = $.proxy(this.options.onDeselectAll, this); | |
| 207 | + this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this); | |
| 208 | + this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); | |
| 209 | + this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); | |
| 210 | + this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); | |
| 211 | + this.options.onInitialized = $.proxy(this.options.onInitialized, this); | |
| 212 | + this.options.onFiltering = $.proxy(this.options.onFiltering, this); | |
| 213 | + | |
| 214 | + // Build select all if enabled. | |
| 215 | + this.buildContainer(); | |
| 216 | + this.buildButton(); | |
| 217 | + this.buildDropdown(); | |
| 218 | + this.buildReset(); | |
| 219 | + this.buildSelectAll(); | |
| 220 | + this.buildDropdownOptions(); | |
| 221 | + this.buildFilter(); | |
| 222 | + this.buildButtons(); | |
| 223 | + | |
| 224 | + this.updateButtonText(); | |
| 225 | + this.updateSelectAll(true); | |
| 226 | + | |
| 227 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 228 | + this.updateOptGroups(); | |
| 229 | + } | |
| 230 | + | |
| 231 | + this.options.wasDisabled = this.$select.prop('disabled'); | |
| 232 | + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0 && !this.options.wasDisabled) { | |
| 233 | + this.disable(true); | |
| 234 | + } | |
| 235 | + | |
| 236 | + this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container); | |
| 237 | + this.$select.prop('tabindex', '-1'); | |
| 238 | + | |
| 239 | + if (this.options.widthSynchronizationMode !== 'never') { | |
| 240 | + this.synchronizeButtonAndPopupWidth(); | |
| 241 | + } | |
| 242 | + | |
| 243 | + this.options.onInitialized(this.$select, this.$container); | |
| 244 | + } | |
| 245 | + | |
| 246 | + Multiselect.prototype = { | |
| 247 | + | |
| 248 | + defaults: { | |
| 249 | + /** | |
| 250 | + * Default text function will either print 'None selected' in case no | |
| 251 | + * option is selected or a list of the selected options up to a length | |
| 252 | + * of 3 selected options. | |
| 253 | + * | |
| 254 | + * @param {jQuery} options | |
| 255 | + * @param {jQuery} select | |
| 256 | + * @returns {String} | |
| 257 | + */ | |
| 258 | + buttonText: function (selectedOptions, select) { | |
| 259 | + if (this.disabledText.length > 0 && select.prop('disabled')) { | |
| 260 | + return this.disabledText; | |
| 261 | + } | |
| 262 | + else if (selectedOptions.length === 0) { | |
| 263 | + return this.nonSelectedText; | |
| 264 | + } | |
| 265 | + else if (this.allSelectedText | |
| 266 | + && selectedOptions.length === $('option', $(select)).length | |
| 267 | + && $('option', $(select)).length !== 1 | |
| 268 | + && this.multiple) { | |
| 269 | + | |
| 270 | + if (this.selectAllNumber) { | |
| 271 | + return this.allSelectedText + ' (' + selectedOptions.length + ')'; | |
| 272 | + } | |
| 273 | + else { | |
| 274 | + return this.allSelectedText; | |
| 275 | + } | |
| 276 | + } | |
| 277 | + else if (this.numberDisplayed != 0 && selectedOptions.length > this.numberDisplayed) { | |
| 278 | + return selectedOptions.length + ' ' + this.nSelectedText; | |
| 279 | + } | |
| 280 | + else { | |
| 281 | + var selected = ''; | |
| 282 | + var delimiter = this.delimiterText; | |
| 283 | + | |
| 284 | + selectedOptions.each(function () { | |
| 285 | + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); | |
| 286 | + selected += label + delimiter; | |
| 287 | + }); | |
| 288 | + | |
| 289 | + return selected.substr(0, selected.length - this.delimiterText.length); | |
| 290 | + } | |
| 291 | + }, | |
| 292 | + /** | |
| 293 | + * Updates the title of the button similar to the buttonText function. | |
| 294 | + * | |
| 295 | + * @param {jQuery} options | |
| 296 | + * @param {jQuery} select | |
| 297 | + * @returns {@exp;selected@call;substr} | |
| 298 | + */ | |
| 299 | + buttonTitle: function (options, select) { | |
| 300 | + if (options.length === 0) { | |
| 301 | + return this.nonSelectedText; | |
| 302 | + } | |
| 303 | + else { | |
| 304 | + var selected = ''; | |
| 305 | + var delimiter = this.delimiterText; | |
| 306 | + | |
| 307 | + options.each(function () { | |
| 308 | + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); | |
| 309 | + selected += label + delimiter; | |
| 310 | + }); | |
| 311 | + return selected.substr(0, selected.length - this.delimiterText.length); | |
| 312 | + } | |
| 313 | + }, | |
| 314 | + checkboxName: function (option) { | |
| 315 | + return false; // no checkbox name | |
| 316 | + }, | |
| 317 | + /** | |
| 318 | + * Create a label. | |
| 319 | + * | |
| 320 | + * @param {jQuery} element | |
| 321 | + * @returns {String} | |
| 322 | + */ | |
| 323 | + optionLabel: function (element) { | |
| 324 | + return $(element).attr('label') || $(element).text(); | |
| 325 | + }, | |
| 326 | + /** | |
| 327 | + * Create a class. | |
| 328 | + * | |
| 329 | + * @param {jQuery} element | |
| 330 | + * @returns {String} | |
| 331 | + */ | |
| 332 | + optionClass: function (element) { | |
| 333 | + return $(element).attr('class') || ''; | |
| 334 | + }, | |
| 335 | + /** | |
| 336 | + * Triggered on change of the multiselect. | |
| 337 | + * | |
| 338 | + * Not triggered when selecting/deselecting options manually. | |
| 339 | + * | |
| 340 | + * @param {jQuery} option | |
| 341 | + * @param {Boolean} checked | |
| 342 | + */ | |
| 343 | + onChange: function (option, checked) { | |
| 344 | + | |
| 345 | + }, | |
| 346 | + /** | |
| 347 | + * Triggered when the dropdown is shown. | |
| 348 | + * | |
| 349 | + * @param {jQuery} event | |
| 350 | + */ | |
| 351 | + onDropdownShow: function (event) { | |
| 352 | + | |
| 353 | + }, | |
| 354 | + /** | |
| 355 | + * Triggered when the dropdown is hidden. | |
| 356 | + * | |
| 357 | + * @param {jQuery} event | |
| 358 | + */ | |
| 359 | + onDropdownHide: function (event) { | |
| 360 | + | |
| 361 | + }, | |
| 362 | + /** | |
| 363 | + * Triggered after the dropdown is shown. | |
| 364 | + * | |
| 365 | + * @param {jQuery} event | |
| 366 | + */ | |
| 367 | + onDropdownShown: function (event) { | |
| 368 | + | |
| 369 | + }, | |
| 370 | + /** | |
| 371 | + * Triggered after the dropdown is hidden. | |
| 372 | + * | |
| 373 | + * @param {jQuery} event | |
| 374 | + */ | |
| 375 | + onDropdownHidden: function (event) { | |
| 376 | + | |
| 377 | + }, | |
| 378 | + /** | |
| 379 | + * Triggered on select all. | |
| 380 | + */ | |
| 381 | + onSelectAll: function () { | |
| 382 | + | |
| 383 | + }, | |
| 384 | + /** | |
| 385 | + * Triggered on deselect all. | |
| 386 | + */ | |
| 387 | + onDeselectAll: function () { | |
| 388 | + | |
| 389 | + }, | |
| 390 | + /** | |
| 391 | + * Triggered after initializing. | |
| 392 | + * | |
| 393 | + * @param {jQuery} $select | |
| 394 | + * @param {jQuery} $container | |
| 395 | + */ | |
| 396 | + onInitialized: function ($select, $container) { | |
| 397 | + | |
| 398 | + }, | |
| 399 | + /** | |
| 400 | + * Triggered on filtering. | |
| 401 | + * | |
| 402 | + * @param {jQuery} $filter | |
| 403 | + */ | |
| 404 | + onFiltering: function ($filter) { | |
| 405 | + | |
| 406 | + }, | |
| 407 | + enableHTML: false, | |
| 408 | + buttonClass: 'custom-select', | |
| 409 | + inheritClass: false, | |
| 410 | + buttonWidth: 'auto', | |
| 411 | + buttonContainer: '<div class="btn-group" />', | |
| 412 | + dropRight: false, | |
| 413 | + dropUp: false, | |
| 414 | + selectedClass: 'active', | |
| 415 | + // Maximum height of the dropdown menu. | |
| 416 | + // If maximum height is exceeded a scrollbar will be displayed. | |
| 417 | + maxHeight: false, | |
| 418 | + includeSelectAllOption: false, | |
| 419 | + includeSelectAllIfMoreThan: 0, | |
| 420 | + selectAllText: ' Select all', | |
| 421 | + selectAllValue: 'multiselect-all', | |
| 422 | + selectAllName: false, | |
| 423 | + selectAllNumber: true, | |
| 424 | + selectAllJustVisible: true, | |
| 425 | + enableFiltering: false, | |
| 426 | + enableCaseInsensitiveFiltering: false, | |
| 427 | + enableFullValueFiltering: false, | |
| 428 | + enableClickableOptGroups: false, | |
| 429 | + enableCollapsibleOptGroups: false, | |
| 430 | + collapseOptGroupsByDefault: false, | |
| 431 | + filterPlaceholder: 'Search', | |
| 432 | + // possible options: 'text', 'value', 'both' | |
| 433 | + filterBehavior: 'text', | |
| 434 | + includeFilterClearBtn: true, | |
| 435 | + preventInputChangeEvent: false, | |
| 436 | + nonSelectedText: 'None selected', | |
| 437 | + nSelectedText: 'selected', | |
| 438 | + allSelectedText: 'All selected', | |
| 439 | + resetButtonText: 'Reset', | |
| 440 | + numberDisplayed: 3, | |
| 441 | + disableIfEmpty: false, | |
| 442 | + disabledText: '', | |
| 443 | + delimiterText: ', ', | |
| 444 | + includeResetOption: false, | |
| 445 | + includeResetDivider: false, | |
| 446 | + resetText: 'Reset', | |
| 447 | + indentGroupOptions: true, | |
| 448 | + // possible options: 'never', 'always', 'ifPopupIsSmaller', 'ifPopupIsWider' | |
| 449 | + widthSynchronizationMode: 'never', | |
| 450 | + buttonTextAlignment: 'center', | |
| 451 | + enableResetButton: false, | |
| 452 | + templates: { | |
| 453 | + button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span></button>', | |
| 454 | + popupContainer: '<div class="multiselect-container dropdown-menu"></div>', | |
| 455 | + filter: '<div class="multiselect-filter d-flex align-items-center"><i class="fas fa-sm fa-search text-muted"></i><input type="search" class="multiselect-search form-control" /></div>', | |
| 456 | + buttonGroup: '<div class="multiselect-buttons btn-group" style="display:flex;"></div>', | |
| 457 | + buttonGroupReset: '<button type="button" class="multiselect-reset btn btn-secondary btn-block"></button>', | |
| 458 | + option: '<button type="button" class="multiselect-option dropdown-item"></button>', | |
| 459 | + divider: '<div class="dropdown-divider"></div>', | |
| 460 | + optionGroup: '<button type="button" class="multiselect-group dropdown-item"></button>', | |
| 461 | + resetButton: '<div class="multiselect-reset text-center p-2"><button type="button" class="btn btn-sm btn-block btn-outline-secondary"></button></div>' | |
| 462 | + } | |
| 463 | + }, | |
| 464 | + | |
| 465 | + constructor: Multiselect, | |
| 466 | + | |
| 467 | + /** | |
| 468 | + * Builds the container of the multiselect. | |
| 469 | + */ | |
| 470 | + buildContainer: function () { | |
| 471 | + this.$container = $(this.options.buttonContainer); | |
| 472 | + if (this.options.widthSynchronizationMode !== 'never') { | |
| 473 | + this.$container.on('show.bs.dropdown', $.proxy(function () { | |
| 474 | + // the width needs to be synchronized again in case the width of the button changed in between | |
| 475 | + this.synchronizeButtonAndPopupWidth(); | |
| 476 | + this.options.onDropdownShow(); | |
| 477 | + }, this)); | |
| 478 | + } | |
| 479 | + else { | |
| 480 | + this.$container.on('show.bs.dropdown', this.options.onDropdownShow); | |
| 481 | + } | |
| 482 | + this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); | |
| 483 | + this.$container.on('shown.bs.dropdown', this.options.onDropdownShown); | |
| 484 | + this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden); | |
| 485 | + }, | |
| 486 | + | |
| 487 | + /** | |
| 488 | + * Builds the button of the multiselect. | |
| 489 | + */ | |
| 490 | + buildButton: function () { | |
| 491 | + this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); | |
| 492 | + if (this.$select.attr('class') && this.options.inheritClass) { | |
| 493 | + this.$button.addClass(this.$select.attr('class')); | |
| 494 | + } | |
| 495 | + // Adopt active state. | |
| 496 | + if (this.$select.prop('disabled')) { | |
| 497 | + this.disable(); | |
| 498 | + } | |
| 499 | + else { | |
| 500 | + this.enable(); | |
| 501 | + } | |
| 502 | + | |
| 503 | + // Manually add button width if set. | |
| 504 | + if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { | |
| 505 | + this.$button.css({ | |
| 506 | + 'width': '100%' //this.options.buttonWidth, | |
| 507 | + }); | |
| 508 | + this.$container.css({ | |
| 509 | + 'width': this.options.buttonWidth | |
| 510 | + }); | |
| 511 | + } | |
| 512 | + | |
| 513 | + if (this.options.buttonTextAlignment) { | |
| 514 | + switch (this.options.buttonTextAlignment) { | |
| 515 | + case 'left': | |
| 516 | + this.$button.addClass('text-left'); | |
| 517 | + break; | |
| 518 | + case 'center': | |
| 519 | + this.$button.addClass('text-center'); | |
| 520 | + break; | |
| 521 | + case 'right': | |
| 522 | + this.$button.addClass('text-right'); | |
| 523 | + break; | |
| 524 | + } | |
| 525 | + } | |
| 526 | + | |
| 527 | + // Keep the tab index from the select. | |
| 528 | + var tabindex = this.$select.attr('tabindex'); | |
| 529 | + if (tabindex) { | |
| 530 | + this.$button.attr('tabindex', tabindex); | |
| 531 | + } | |
| 532 | + | |
| 533 | + this.$container.prepend(this.$button); | |
| 534 | + }, | |
| 535 | + | |
| 536 | + /** | |
| 537 | + * Builds the popup container representing the dropdown menu. | |
| 538 | + */ | |
| 539 | + buildDropdown: function () { | |
| 540 | + | |
| 541 | + // Build popup container. | |
| 542 | + this.$popupContainer = $(this.options.templates.popupContainer); | |
| 543 | + | |
| 544 | + if (this.options.dropRight) { | |
| 545 | + this.$container.addClass('dropright'); | |
| 546 | + } | |
| 547 | + else if (this.options.dropUp) { | |
| 548 | + this.$container.addClass("dropup"); | |
| 549 | + } | |
| 550 | + | |
| 551 | + // Set max height of dropdown menu to activate auto scrollbar. | |
| 552 | + if (this.options.maxHeight) { | |
| 553 | + // TODO: Add a class for this option to move the css declarations. | |
| 554 | + this.$popupContainer.css({ | |
| 555 | + 'max-height': this.options.maxHeight + 'px', | |
| 556 | + 'overflow-y': 'auto', | |
| 557 | + 'overflow-x': 'hidden' | |
| 558 | + }); | |
| 559 | + } | |
| 560 | + | |
| 561 | + if (this.options.widthSynchronizationMode !== 'never') { | |
| 562 | + this.$popupContainer.css('overflow-x', 'hidden'); | |
| 563 | + } | |
| 564 | + | |
| 565 | + this.$popupContainer.on("touchstart click", function (e) { | |
| 566 | + e.stopPropagation(); | |
| 567 | + }); | |
| 568 | + | |
| 569 | + this.$container.append(this.$popupContainer); | |
| 570 | + }, | |
| 571 | + | |
| 572 | + synchronizeButtonAndPopupWidth: function () { | |
| 573 | + if (!this.$popupContainer || this.options.widthSynchronizationMode === 'never') { | |
| 574 | + return; | |
| 575 | + } | |
| 576 | + | |
| 577 | + var buttonWidth = this.$button.outerWidth(); | |
| 578 | + switch (this.options.widthSynchronizationMode) { | |
| 579 | + case 'always': | |
| 580 | + this.$popupContainer.css('min-width', buttonWidth); | |
| 581 | + this.$popupContainer.css('max-width', buttonWidth); | |
| 582 | + break; | |
| 583 | + case 'ifPopupIsSmaller': | |
| 584 | + this.$popupContainer.css('min-width', buttonWidth); | |
| 585 | + break; | |
| 586 | + case 'ifPopupIsWider': | |
| 587 | + this.$popupContainer.css('max-width', buttonWidth); | |
| 588 | + break; | |
| 589 | + } | |
| 590 | + }, | |
| 591 | + | |
| 592 | + /** | |
| 593 | + * Build the dropdown options and binds all necessary events. | |
| 594 | + * | |
| 595 | + * Uses createDivider and createOptionValue to create the necessary options. | |
| 596 | + */ | |
| 597 | + buildDropdownOptions: function () { | |
| 598 | + | |
| 599 | + this.$select.children().each($.proxy(function (index, element) { | |
| 600 | + | |
| 601 | + var $element = $(element); | |
| 602 | + // Support optgroups and options without a group simultaneously. | |
| 603 | + var tag = $element.prop('tagName') | |
| 604 | + .toLowerCase(); | |
| 605 | + | |
| 606 | + if ($element.prop('value') === this.options.selectAllValue) { | |
| 607 | + return; | |
| 608 | + } | |
| 609 | + | |
| 610 | + if (tag === 'optgroup') { | |
| 611 | + this.createOptgroup(element); | |
| 612 | + } | |
| 613 | + else if (tag === 'option') { | |
| 614 | + | |
| 615 | + if ($element.data('role') === 'divider') { | |
| 616 | + this.createDivider(); | |
| 617 | + } | |
| 618 | + else { | |
| 619 | + this.createOptionValue(element, false); | |
| 620 | + } | |
| 621 | + | |
| 622 | + } | |
| 623 | + | |
| 624 | + // Other illegal tags will be ignored. | |
| 625 | + }, this)); | |
| 626 | + | |
| 627 | + // Bind the change event on the dropdown elements. | |
| 628 | + $(this.$popupContainer).off('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]'); | |
| 629 | + $(this.$popupContainer).on('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]', $.proxy(function (event) { | |
| 630 | + var $target = $(event.target); | |
| 631 | + | |
| 632 | + var checked = $target.prop('checked') || false; | |
| 633 | + var isSelectAllOption = $target.val() === this.options.selectAllValue; | |
| 634 | + | |
| 635 | + // Apply or unapply the configured selected class. | |
| 636 | + if (this.options.selectedClass) { | |
| 637 | + if (checked) { | |
| 638 | + $target.closest('.multiselect-option') | |
| 639 | + .addClass(this.options.selectedClass); | |
| 640 | + } | |
| 641 | + else { | |
| 642 | + $target.closest('.multiselect-option') | |
| 643 | + .removeClass(this.options.selectedClass); | |
| 644 | + } | |
| 645 | + } | |
| 646 | + | |
| 647 | + // Get the corresponding option. | |
| 648 | + var value = $target.val(); | |
| 649 | + var $option = this.getOptionByValue(value); | |
| 650 | + | |
| 651 | + var $optionsNotThis = $('option', this.$select).not($option); | |
| 652 | + var $checkboxesNotThis = $('input', this.$container).not($target); | |
| 653 | + | |
| 654 | + if (isSelectAllOption) { | |
| 655 | + | |
| 656 | + if (checked) { | |
| 657 | + this.selectAll(this.options.selectAllJustVisible, true); | |
| 658 | + } | |
| 659 | + else { | |
| 660 | + this.deselectAll(this.options.selectAllJustVisible, true); | |
| 661 | + } | |
| 662 | + } | |
| 663 | + else { | |
| 664 | + if (checked) { | |
| 665 | + $option.prop('selected', true); | |
| 666 | + | |
| 667 | + if (this.options.multiple) { | |
| 668 | + // Simply select additional option. | |
| 669 | + $option.prop('selected', true); | |
| 670 | + } | |
| 671 | + else { | |
| 672 | + // Unselect all other options and corresponding checkboxes. | |
| 673 | + if (this.options.selectedClass) { | |
| 674 | + $($checkboxesNotThis).closest('.dropdown-item').removeClass(this.options.selectedClass); | |
| 675 | + } | |
| 676 | + | |
| 677 | + $($checkboxesNotThis).prop('checked', false); | |
| 678 | + $optionsNotThis.prop('selected', false); | |
| 679 | + | |
| 680 | + // It's a single selection, so close. | |
| 681 | + this.$button.click(); | |
| 682 | + } | |
| 683 | + | |
| 684 | + if (this.options.selectedClass === "active") { | |
| 685 | + $optionsNotThis.closest(".dropdown-item").css("outline", ""); | |
| 686 | + } | |
| 687 | + } | |
| 688 | + else { | |
| 689 | + // Unselect option. | |
| 690 | + $option.prop('selected', false); | |
| 691 | + } | |
| 692 | + | |
| 693 | + // To prevent select all from firing onChange: #575 | |
| 694 | + this.options.onChange($option, checked); | |
| 695 | + | |
| 696 | + // Do not update select all or optgroups on select all change! | |
| 697 | + this.updateSelectAll(); | |
| 698 | + | |
| 699 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 700 | + this.updateOptGroups(); | |
| 701 | + } | |
| 702 | + } | |
| 703 | + | |
| 704 | + this.$select.change(); | |
| 705 | + this.updateButtonText(); | |
| 706 | + | |
| 707 | + if (this.options.preventInputChangeEvent) { | |
| 708 | + return false; | |
| 709 | + } | |
| 710 | + }, this)); | |
| 711 | + | |
| 712 | + $('.multiselect-option', this.$popupContainer).off('mousedown'); | |
| 713 | + $('.multiselect-option', this.$popupContainer).on('mousedown', function (e) { | |
| 714 | + if (e.shiftKey) { | |
| 715 | + // Prevent selecting text by Shift+click | |
| 716 | + return false; | |
| 717 | + } | |
| 718 | + }); | |
| 719 | + | |
| 720 | + $(this.$popupContainer).off('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group'); | |
| 721 | + $(this.$popupContainer).on('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group', $.proxy(function (event) { | |
| 722 | + event.stopPropagation(); | |
| 723 | + | |
| 724 | + var $target = $(event.target); | |
| 725 | + | |
| 726 | + if (event.shiftKey && this.options.multiple) { | |
| 727 | + if (!$target.is("input")) { // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) | |
| 728 | + event.preventDefault(); | |
| 729 | + $target = $target.closest(".multiselect-option").find("input"); | |
| 730 | + $target.prop("checked", !$target.prop("checked")); | |
| 731 | + } | |
| 732 | + var checked = $target.prop('checked') || false; | |
| 733 | + | |
| 734 | + if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range | |
| 735 | + var from = this.$popupContainer.find(".multiselect-option:visible").index($target.closest(".multiselect-option")); | |
| 736 | + var to = this.$popupContainer.find(".multiselect-option:visible").index(this.lastToggledInput.closest(".multiselect-option")); | |
| 737 | + | |
| 738 | + if (from > to) { // Swap the indices | |
| 739 | + var tmp = to; | |
| 740 | + to = from; | |
| 741 | + from = tmp; | |
| 742 | + } | |
| 743 | + | |
| 744 | + // Make sure we grab all elements since slice excludes the last index | |
| 745 | + ++to; | |
| 746 | + | |
| 747 | + // Change the checkboxes and underlying options | |
| 748 | + var range = this.$popupContainer.find(".multiselect-option:not(.multiselect-filter-hidden)").slice(from, to).find("input"); | |
| 749 | + | |
| 750 | + range.prop('checked', checked); | |
| 751 | + | |
| 752 | + if (this.options.selectedClass) { | |
| 753 | + range.closest('.multiselect-option') | |
| 754 | + .toggleClass(this.options.selectedClass, checked); | |
| 755 | + } | |
| 756 | + | |
| 757 | + for (var i = 0, j = range.length; i < j; i++) { | |
| 758 | + var $checkbox = $(range[i]); | |
| 759 | + | |
| 760 | + var $option = this.getOptionByValue($checkbox.val()); | |
| 761 | + | |
| 762 | + $option.prop('selected', checked); | |
| 763 | + } | |
| 764 | + } | |
| 765 | + | |
| 766 | + // Trigger the select "change" event | |
| 767 | + $target.trigger("change"); | |
| 768 | + } | |
| 769 | + else if (!$target.is('input')) { | |
| 770 | + var $checkbox = $target.closest('.multiselect-option, .multiselect-all').find('.form-check-input'); | |
| 771 | + if ($checkbox.length > 0) { | |
| 772 | + if (this.options.multiple || !$checkbox.prop('checked')) { | |
| 773 | + $checkbox.prop('checked', !$checkbox.prop('checked')); | |
| 774 | + $checkbox.change(); | |
| 775 | + } | |
| 776 | + } | |
| 777 | + else if (this.options.enableClickableOptGroups && this.options.multiple && !$target.hasClass("caret-container")) { | |
| 778 | + var groupItem = $target; | |
| 779 | + if (!groupItem.hasClass("multiselect-group")) { | |
| 780 | + groupItem = $target.closest('.multiselect-group'); | |
| 781 | + } | |
| 782 | + $checkbox = groupItem.find(".form-check-input"); | |
| 783 | + if ($checkbox.length > 0) { | |
| 784 | + $checkbox.prop('checked', !$checkbox.prop('checked')); | |
| 785 | + $checkbox.change(); | |
| 786 | + } | |
| 787 | + } | |
| 788 | + | |
| 789 | + event.preventDefault(); | |
| 790 | + } | |
| 791 | + | |
| 792 | + // Remembers last clicked option | |
| 793 | + var $input = $target.closest(".multiselect-option").find("input[type='checkbox'], input[type='radio']"); | |
| 794 | + if ($input.length > 0) { | |
| 795 | + this.lastToggledInput = $target; | |
| 796 | + } | |
| 797 | + else { | |
| 798 | + this.lastToggledInput = null; | |
| 799 | + } | |
| 800 | + | |
| 801 | + $target.blur(); | |
| 802 | + }, this)); | |
| 803 | + | |
| 804 | + //Keyboard support. | |
| 805 | + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function (event) { | |
| 806 | + var $items = $(this.$container).find(".multiselect-option:not(.disabled), .multiselect-group:not(.disabled), .multiselect-all").filter(":visible"); | |
| 807 | + var index = $items.index($items.filter(':focus')); | |
| 808 | + var $search = $('.multiselect-search', this.$container); | |
| 809 | + | |
| 810 | + // keyCode 9 == Tab | |
| 811 | + if (event.keyCode === 9 && this.$container.hasClass('show')) { | |
| 812 | + this.$button.click(); | |
| 813 | + } | |
| 814 | + // keyCode 13 = Enter | |
| 815 | + else if (event.keyCode == 13) { | |
| 816 | + var $current = $items.eq(index); | |
| 817 | + setTimeout(function () { | |
| 818 | + $current.focus(); | |
| 819 | + }, 1); | |
| 820 | + } | |
| 821 | + // keyCode 38 = Arrow Up | |
| 822 | + else if (event.keyCode == 38) { | |
| 823 | + if (index == 0 && !$search.is(':focus')) { | |
| 824 | + setTimeout(function () { | |
| 825 | + $search.focus(); | |
| 826 | + }, 1); | |
| 827 | + } | |
| 828 | + } | |
| 829 | + // keyCode 40 = Arrow Down | |
| 830 | + else if (event.keyCode == 40) { | |
| 831 | + if ($search.is(':focus')) { | |
| 832 | + var $first = $items.eq(0); | |
| 833 | + setTimeout(function () { | |
| 834 | + $search.blur(); | |
| 835 | + $first.focus(); | |
| 836 | + }, 1); | |
| 837 | + } | |
| 838 | + else if (index == -1) { | |
| 839 | + setTimeout(function () { | |
| 840 | + $search.focus(); | |
| 841 | + }, 1); | |
| 842 | + } | |
| 843 | + } | |
| 844 | + }, this)); | |
| 845 | + | |
| 846 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 847 | + $(".multiselect-group input", this.$popupContainer).off("change"); | |
| 848 | + $(".multiselect-group input", this.$popupContainer).on("change", $.proxy(function (event) { | |
| 849 | + event.stopPropagation(); | |
| 850 | + | |
| 851 | + var $target = $(event.target); | |
| 852 | + var checked = $target.prop('checked') || false; | |
| 853 | + | |
| 854 | + var $item = $(event.target).closest('.dropdown-item'); | |
| 855 | + var $group = $item.nextUntil(".multiselect-group") | |
| 856 | + .not('.multiselect-filter-hidden') | |
| 857 | + .not('.disabled'); | |
| 858 | + | |
| 859 | + var $inputs = $group.find("input"); | |
| 860 | + | |
| 861 | + var $options = []; | |
| 862 | + | |
| 863 | + if (this.options.selectedClass) { | |
| 864 | + if (checked) { | |
| 865 | + $item.addClass(this.options.selectedClass); | |
| 866 | + } | |
| 867 | + else { | |
| 868 | + $item.removeClass(this.options.selectedClass); | |
| 869 | + } | |
| 870 | + } | |
| 871 | + | |
| 872 | + $.each($inputs, $.proxy(function (index, input) { | |
| 873 | + var $input = $(input); | |
| 874 | + var value = $input.val(); | |
| 875 | + var $option = this.getOptionByValue(value); | |
| 876 | + | |
| 877 | + if (checked) { | |
| 878 | + $input.prop('checked', true); | |
| 879 | + $input.closest('.dropdown-item') | |
| 880 | + .addClass(this.options.selectedClass); | |
| 881 | + | |
| 882 | + $option.prop('selected', true); | |
| 883 | + } | |
| 884 | + else { | |
| 885 | + $input.prop('checked', false); | |
| 886 | + $input.closest('.dropdown-item') | |
| 887 | + .removeClass(this.options.selectedClass); | |
| 888 | + | |
| 889 | + $option.prop('selected', false); | |
| 890 | + } | |
| 891 | + | |
| 892 | + $options.push(this.getOptionByValue(value)); | |
| 893 | + }, this)) | |
| 894 | + | |
| 895 | + // Cannot use select or deselect here because it would call updateOptGroups again. | |
| 896 | + | |
| 897 | + this.options.onChange($options, checked); | |
| 898 | + | |
| 899 | + this.$select.change(); | |
| 900 | + this.updateButtonText(); | |
| 901 | + this.updateSelectAll(); | |
| 902 | + }, this)); | |
| 903 | + } | |
| 904 | + | |
| 905 | + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { | |
| 906 | + $(".multiselect-group .caret-container", this.$popupContainer).off("click"); | |
| 907 | + $(".multiselect-group .caret-container", this.$popupContainer).on("click", $.proxy(function (event) { | |
| 908 | + var $group = $(event.target).closest('.multiselect-group'); | |
| 909 | + var $inputs = $group.nextUntil(".multiselect-group") | |
| 910 | + .not('.multiselect-filter-hidden'); | |
| 911 | + | |
| 912 | + var visible = true; | |
| 913 | + $inputs.each(function () { | |
| 914 | + visible = visible && !$(this).hasClass('multiselect-collapsible-hidden'); | |
| 915 | + }); | |
| 916 | + | |
| 917 | + if (visible) { | |
| 918 | + $inputs.hide() | |
| 919 | + .addClass('multiselect-collapsible-hidden'); | |
| 920 | + } | |
| 921 | + else { | |
| 922 | + $inputs.show() | |
| 923 | + .removeClass('multiselect-collapsible-hidden'); | |
| 924 | + } | |
| 925 | + }, this)); | |
| 926 | + } | |
| 927 | + }, | |
| 928 | + | |
| 929 | + /** | |
| 930 | + * Create a checkbox container with input and label based on given values | |
| 931 | + * @param {JQuery} $item | |
| 932 | + * @param {String} label | |
| 933 | + * @param {String} name | |
| 934 | + * @param {String} value | |
| 935 | + * @param {String} inputType | |
| 936 | + * @returns {JQuery} | |
| 937 | + */ | |
| 938 | + createCheckbox: function ($item, labelContent, name, value, title, inputType) { | |
| 939 | + var $wrapper = $('<span />'); | |
| 940 | + $wrapper.addClass("form-check"); | |
| 941 | + | |
| 942 | + if (this.options.enableHTML && $(labelContent).length > 0) { | |
| 943 | + var $checkboxLabel = $('<label class="form-check-label" />'); | |
| 944 | + $checkboxLabel.html(labelContent); | |
| 945 | + $wrapper.append($checkboxLabel); | |
| 946 | + } | |
| 947 | + else { | |
| 948 | + var $checkboxLabel = $('<label class="form-check-label" />'); | |
| 949 | + $checkboxLabel.text(labelContent); | |
| 950 | + $wrapper.append($checkboxLabel); | |
| 951 | + } | |
| 952 | + | |
| 953 | + var $checkbox = $('<input class="form-check-input"/>').attr('type', inputType); | |
| 954 | + $checkbox.val(value); | |
| 955 | + $wrapper.prepend($checkbox); | |
| 956 | + | |
| 957 | + if (name) { | |
| 958 | + $checkbox.attr('name', name); | |
| 959 | + } | |
| 960 | + | |
| 961 | + $item.prepend($wrapper); | |
| 962 | + $item.attr("title", title || labelContent); | |
| 963 | + | |
| 964 | + return $checkbox; | |
| 965 | + }, | |
| 966 | + | |
| 967 | + /** | |
| 968 | + * Create an option using the given select option. | |
| 969 | + * | |
| 970 | + * @param {jQuery} element | |
| 971 | + */ | |
| 972 | + createOptionValue: function (element, isGroupOption) { | |
| 973 | + var $element = $(element); | |
| 974 | + if ($element.is(':selected')) { | |
| 975 | + $element.prop('selected', true); | |
| 976 | + } | |
| 977 | + | |
| 978 | + // Support the label attribute on options. | |
| 979 | + var label = this.options.optionLabel(element); | |
| 980 | + var classes = this.options.optionClass(element); | |
| 981 | + var value = $element.val(); | |
| 982 | + var inputType = this.options.multiple ? "checkbox" : "radio"; | |
| 983 | + var title = $element.attr('title'); | |
| 984 | + | |
| 985 | + var $option = $(this.options.templates.option); | |
| 986 | + $option.addClass(classes); | |
| 987 | + | |
| 988 | + if (isGroupOption && this.options.indentGroupOptions) { | |
| 989 | + $option.addClass("multiselect-group-option-indented") | |
| 990 | + } | |
| 991 | + | |
| 992 | + // Hide all children items when collapseOptGroupsByDefault is true | |
| 993 | + if (this.options.collapseOptGroupsByDefault && $(element).parent().prop("tagName").toLowerCase() === "optgroup") { | |
| 994 | + $option.addClass("multiselect-collapsible-hidden"); | |
| 995 | + $option.hide(); | |
| 996 | + } | |
| 997 | + | |
| 998 | + var name = this.options.checkboxName($element); | |
| 999 | + var $checkbox = this.createCheckbox($option, label, name, value, title, inputType); | |
| 1000 | + | |
| 1001 | + var selected = $element.prop('selected') || false; | |
| 1002 | + | |
| 1003 | + if (value === this.options.selectAllValue) { | |
| 1004 | + $option.addClass("multiselect-all"); | |
| 1005 | + $option.removeClass("multiselect-option"); | |
| 1006 | + $checkbox.parent().parent() | |
| 1007 | + .addClass('multiselect-all'); | |
| 1008 | + } | |
| 1009 | + | |
| 1010 | + this.$popupContainer.append($option); | |
| 1011 | + | |
| 1012 | + if ($element.is(':disabled')) { | |
| 1013 | + $checkbox.attr('disabled', 'disabled') | |
| 1014 | + .prop('disabled', true) | |
| 1015 | + .closest('.dropdown-item') | |
| 1016 | + .addClass('disabled'); | |
| 1017 | + } | |
| 1018 | + | |
| 1019 | + $checkbox.prop('checked', selected); | |
| 1020 | + | |
| 1021 | + if (selected && this.options.selectedClass) { | |
| 1022 | + $checkbox.closest('.dropdown-item') | |
| 1023 | + .addClass(this.options.selectedClass); | |
| 1024 | + } | |
| 1025 | + }, | |
| 1026 | + | |
| 1027 | + /** | |
| 1028 | + * Creates a divider using the given select option. | |
| 1029 | + * | |
| 1030 | + * @param {jQuery} element | |
| 1031 | + */ | |
| 1032 | + createDivider: function (element) { | |
| 1033 | + var $divider = $(this.options.templates.divider); | |
| 1034 | + this.$popupContainer.append($divider); | |
| 1035 | + }, | |
| 1036 | + | |
| 1037 | + /** | |
| 1038 | + * Creates an optgroup. | |
| 1039 | + * | |
| 1040 | + * @param {jQuery} group | |
| 1041 | + */ | |
| 1042 | + createOptgroup: function (group) { | |
| 1043 | + var $group = $(group); | |
| 1044 | + var label = $group.attr("label"); | |
| 1045 | + var value = $group.attr("value"); | |
| 1046 | + var title = $group.attr('title'); | |
| 1047 | + | |
| 1048 | + var $groupOption = $("<span class='multiselect-group dropdown-item-text'></span>"); | |
| 1049 | + | |
| 1050 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1051 | + $groupOption = $(this.options.templates.optionGroup); | |
| 1052 | + var $checkbox = this.createCheckbox($groupOption, label, null, value, title, "checkbox"); | |
| 1053 | + } | |
| 1054 | + else { | |
| 1055 | + if (this.options.enableHTML) { | |
| 1056 | + $groupOption.html(" " + label); | |
| 1057 | + } | |
| 1058 | + else { | |
| 1059 | + $groupOption.text(" " + label); | |
| 1060 | + } | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + var classes = this.options.optionClass(group); | |
| 1064 | + $groupOption.addClass(classes); | |
| 1065 | + | |
| 1066 | + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { | |
| 1067 | + $groupOption.find('.form-check').addClass('d-inline-block'); | |
| 1068 | + $groupOption.append('<span class="caret-container dropdown-toggle pl-1"></span>'); | |
| 1069 | + } | |
| 1070 | + | |
| 1071 | + if ($group.is(':disabled')) { | |
| 1072 | + $groupOption.addClass('disabled'); | |
| 1073 | + } | |
| 1074 | + | |
| 1075 | + this.$popupContainer.append($groupOption); | |
| 1076 | + | |
| 1077 | + $("option", group).each($.proxy(function ($, group) { | |
| 1078 | + this.createOptionValue(group, true); | |
| 1079 | + }, this)); | |
| 1080 | + }, | |
| 1081 | + | |
| 1082 | + /** | |
| 1083 | + * Build the reset. | |
| 1084 | + * | |
| 1085 | + */ | |
| 1086 | + buildReset: function () { | |
| 1087 | + if (this.options.includeResetOption) { | |
| 1088 | + | |
| 1089 | + // Check whether to add a divider after the reset. | |
| 1090 | + if (this.options.includeResetDivider) { | |
| 1091 | + var divider = $(this.options.templates.divider); | |
| 1092 | + divider.addClass("mt-0"); | |
| 1093 | + this.$popupContainer.prepend(divider); | |
| 1094 | + } | |
| 1095 | + | |
| 1096 | + var $resetButton = $(this.options.templates.resetButton); | |
| 1097 | + | |
| 1098 | + if (this.options.enableHTML) { | |
| 1099 | + $('button', $resetButton).html(this.options.resetText); | |
| 1100 | + } | |
| 1101 | + else { | |
| 1102 | + $('button', $resetButton).text(this.options.resetText); | |
| 1103 | + } | |
| 1104 | + | |
| 1105 | + $('button', $resetButton).click($.proxy(function () { | |
| 1106 | + this.clearSelection(); | |
| 1107 | + }, this)); | |
| 1108 | + | |
| 1109 | + this.$popupContainer.prepend($resetButton); | |
| 1110 | + } | |
| 1111 | + }, | |
| 1112 | + | |
| 1113 | + /** | |
| 1114 | + * Build the select all. | |
| 1115 | + * | |
| 1116 | + * Checks if a select all has already been created. | |
| 1117 | + */ | |
| 1118 | + buildSelectAll: function () { | |
| 1119 | + if (typeof this.options.selectAllValue === 'number') { | |
| 1120 | + this.options.selectAllValue = this.options.selectAllValue.toString(); | |
| 1121 | + } | |
| 1122 | + | |
| 1123 | + var alreadyHasSelectAll = this.hasSelectAll(); | |
| 1124 | + | |
| 1125 | + if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple | |
| 1126 | + && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { | |
| 1127 | + | |
| 1128 | + // Check whether to add a divider after the select all. | |
| 1129 | + if (this.options.includeSelectAllDivider) { | |
| 1130 | + this.$popupContainer.prepend($(this.options.templates.divider)); | |
| 1131 | + } | |
| 1132 | + | |
| 1133 | + var $option = $(this.options.templates.li || this.options.templates.option); | |
| 1134 | + var $checkbox = this.createCheckbox($option, this.options.selectAllText, this.options.selectAllName, this.options.selectAllValue, this.options.selectAllText, "checkbox"); | |
| 1135 | + | |
| 1136 | + $option.addClass("multiselect-all"); | |
| 1137 | + $option.removeClass("multiselect-option"); | |
| 1138 | + $option.find(".form-check-label").addClass("font-weight-bold"); | |
| 1139 | + | |
| 1140 | + this.$popupContainer.prepend($option); | |
| 1141 | + | |
| 1142 | + $checkbox.prop('checked', false); | |
| 1143 | + } | |
| 1144 | + }, | |
| 1145 | + | |
| 1146 | + /** | |
| 1147 | + * Builds the filter. | |
| 1148 | + */ | |
| 1149 | + buildFilter: function () { | |
| 1150 | + | |
| 1151 | + // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. | |
| 1152 | + if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { | |
| 1153 | + var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); | |
| 1154 | + | |
| 1155 | + if (this.$select.find('option').length >= enableFilterLength) { | |
| 1156 | + | |
| 1157 | + this.$filter = $(this.options.templates.filter); | |
| 1158 | + $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); | |
| 1159 | + | |
| 1160 | + // Handles optional filter clear button | |
| 1161 | + if (!this.options.includeFilterClearBtn) { | |
| 1162 | + this.$filter.find(".multiselect-search").attr("type", "text"); | |
| 1163 | + | |
| 1164 | + // Remove clear button if the old design of the filter with input groups and separated clear button is used | |
| 1165 | + this.$filter.find(".multiselect-clear-filter").remove(); | |
| 1166 | + } | |
| 1167 | + else { | |
| 1168 | + // Firefox does not support a clear button in search inputs right now therefore it must be added manually | |
| 1169 | + if (this.isFirefox() && this.$filter.find(".multiselect-clear-filter").length === 0) { | |
| 1170 | + this.$filter.append("<i class='fas fa-times text-muted multiselect-clear-filter multiselect-moz-clear-filter'></i>"); | |
| 1171 | + } | |
| 1172 | + | |
| 1173 | + this.$filter.find(".multiselect-clear-filter").on('click', $.proxy(function (event) { | |
| 1174 | + clearTimeout(this.searchTimeout); | |
| 1175 | + | |
| 1176 | + this.query = ''; | |
| 1177 | + this.$filter.find('.multiselect-search').val(''); | |
| 1178 | + $('.dropdown-item', this.$popupContainer).show().removeClass('multiselect-filter-hidden'); | |
| 1179 | + | |
| 1180 | + this.updateSelectAll(); | |
| 1181 | + | |
| 1182 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1183 | + this.updateOptGroups(); | |
| 1184 | + } | |
| 1185 | + | |
| 1186 | + }, this)); | |
| 1187 | + } | |
| 1188 | + | |
| 1189 | + this.$popupContainer.prepend(this.$filter); | |
| 1190 | + | |
| 1191 | + this.$filter.val(this.query).on('click', function (event) { | |
| 1192 | + event.stopPropagation(); | |
| 1193 | + }).on('input keydown', $.proxy(function (event) { | |
| 1194 | + // Cancel enter key default behaviour | |
| 1195 | + if (event.which === 13) { | |
| 1196 | + event.preventDefault(); | |
| 1197 | + } | |
| 1198 | + | |
| 1199 | + if (this.isFirefox() && this.options.includeFilterClearBtn) { | |
| 1200 | + if (event.target.value) { | |
| 1201 | + this.$filter.find(".multiselect-moz-clear-filter").show(); | |
| 1202 | + } | |
| 1203 | + else { | |
| 1204 | + this.$filter.find(".multiselect-moz-clear-filter").hide(); | |
| 1205 | + } | |
| 1206 | + } | |
| 1207 | + | |
| 1208 | + // This is useful to catch "keydown" events after the browser has updated the control. | |
| 1209 | + clearTimeout(this.searchTimeout); | |
| 1210 | + | |
| 1211 | + this.searchTimeout = this.asyncFunction($.proxy(function () { | |
| 1212 | + | |
| 1213 | + if (this.query !== event.target.value) { | |
| 1214 | + this.query = event.target.value; | |
| 1215 | + | |
| 1216 | + var currentGroup, currentGroupVisible; | |
| 1217 | + $.each($('.multiselect-option, .multiselect-group', this.$popupContainer), $.proxy(function (index, element) { | |
| 1218 | + var value = $('input', element).length > 0 ? $('input', element).val() : ""; | |
| 1219 | + var text = $('.form-check-label', element).text(); | |
| 1220 | + | |
| 1221 | + var filterCandidate = ''; | |
| 1222 | + if ((this.options.filterBehavior === 'text')) { | |
| 1223 | + filterCandidate = text; | |
| 1224 | + } | |
| 1225 | + else if ((this.options.filterBehavior === 'value')) { | |
| 1226 | + filterCandidate = value; | |
| 1227 | + } | |
| 1228 | + else if (this.options.filterBehavior === 'both') { | |
| 1229 | + filterCandidate = text + '\n' + value; | |
| 1230 | + } | |
| 1231 | + | |
| 1232 | + if (value !== this.options.selectAllValue && text) { | |
| 1233 | + | |
| 1234 | + // By default lets assume that element is not | |
| 1235 | + // interesting for this search. | |
| 1236 | + var showElement = false; | |
| 1237 | + | |
| 1238 | + if (this.options.enableCaseInsensitiveFiltering) { | |
| 1239 | + filterCandidate = filterCandidate.toLowerCase(); | |
| 1240 | + this.query = this.query.toLowerCase(); | |
| 1241 | + } | |
| 1242 | + | |
| 1243 | + if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { | |
| 1244 | + var valueToMatch = filterCandidate.trim().substring(0, this.query.length); | |
| 1245 | + if (this.query.indexOf(valueToMatch) > -1) { | |
| 1246 | + showElement = true; | |
| 1247 | + } | |
| 1248 | + } | |
| 1249 | + else if (filterCandidate.indexOf(this.query) > -1) { | |
| 1250 | + showElement = true; | |
| 1251 | + } | |
| 1252 | + | |
| 1253 | + // Toggle current element (group or group item) according to showElement boolean. | |
| 1254 | + if (!showElement) { | |
| 1255 | + $(element).css('display', 'none'); | |
| 1256 | + $(element).addClass('multiselect-filter-hidden'); | |
| 1257 | + } | |
| 1258 | + if (showElement) { | |
| 1259 | + $(element).css('display', 'block'); | |
| 1260 | + $(element).removeClass('multiselect-filter-hidden'); | |
| 1261 | + } | |
| 1262 | + | |
| 1263 | + // Differentiate groups and group items. | |
| 1264 | + if ($(element).hasClass('multiselect-group')) { | |
| 1265 | + // Remember group status. | |
| 1266 | + currentGroup = element; | |
| 1267 | + currentGroupVisible = showElement; | |
| 1268 | + } | |
| 1269 | + else { | |
| 1270 | + // Show group name when at least one of its items is visible. | |
| 1271 | + if (showElement) { | |
| 1272 | + $(currentGroup).show() | |
| 1273 | + .removeClass('multiselect-filter-hidden'); | |
| 1274 | + } | |
| 1275 | + | |
| 1276 | + // Show all group items when group name satisfies filter. | |
| 1277 | + if (!showElement && currentGroupVisible) { | |
| 1278 | + $(element).show() | |
| 1279 | + .removeClass('multiselect-filter-hidden'); | |
| 1280 | + } | |
| 1281 | + } | |
| 1282 | + } | |
| 1283 | + }, this)); | |
| 1284 | + } | |
| 1285 | + | |
| 1286 | + this.updateSelectAll(); | |
| 1287 | + | |
| 1288 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1289 | + this.updateOptGroups(); | |
| 1290 | + } | |
| 1291 | + | |
| 1292 | + this.updatePopupPosition(); | |
| 1293 | + | |
| 1294 | + this.options.onFiltering(event.target); | |
| 1295 | + | |
| 1296 | + }, this), 300, this); | |
| 1297 | + }, this)); | |
| 1298 | + } | |
| 1299 | + } | |
| 1300 | + }, | |
| 1301 | + | |
| 1302 | + /** | |
| 1303 | + * Builds the filter. | |
| 1304 | + */ | |
| 1305 | + buildButtons: function () { | |
| 1306 | + if (this.options.enableResetButton) { | |
| 1307 | + var $buttonGroup = $(this.options.templates.buttonGroup); | |
| 1308 | + this.$buttonGroupReset = $(this.options.templates.buttonGroupReset).text(this.options.resetButtonText); | |
| 1309 | + $buttonGroup.append(this.$buttonGroupReset); | |
| 1310 | + this.$popupContainer.prepend($buttonGroup); | |
| 1311 | + | |
| 1312 | + // We save all options that were previously selected. | |
| 1313 | + this.defaultSelection = {}; | |
| 1314 | + $('option', this.$select).each($.proxy(function(index, element) { | |
| 1315 | + var $option = $(element); | |
| 1316 | + this.defaultSelection[$option.val()] = $option.prop('selected'); | |
| 1317 | + }, this)); | |
| 1318 | + | |
| 1319 | + this.$buttonGroupReset.on('click', $.proxy(function(event) { | |
| 1320 | + $('option', this.$select).each($.proxy(function(index, element) { | |
| 1321 | + var $option = $(element); | |
| 1322 | + $option.prop('selected', this.defaultSelection[$option.val()]); | |
| 1323 | + }, this)); | |
| 1324 | + this.refresh(); | |
| 1325 | + | |
| 1326 | + if (this.options.enableFiltering) { | |
| 1327 | + this.$filter.trigger('keydown'); | |
| 1328 | + $('input', this.$filter).val(''); | |
| 1329 | + } | |
| 1330 | + }, this)); | |
| 1331 | + } | |
| 1332 | + }, | |
| 1333 | + | |
| 1334 | + updatePopupPosition: function() { | |
| 1335 | + // prevent gaps between popup and select when filter is used (#1199) | |
| 1336 | + var transformMatrix = this.$popupContainer.css("transform"); | |
| 1337 | + var matrixType = transformMatrix.substring(0, transformMatrix.indexOf('(')); | |
| 1338 | + var values = transformMatrix.substring(transformMatrix.indexOf('(') + 1, transformMatrix.length - 1); | |
| 1339 | + var valuesArray = values.split(','); | |
| 1340 | + | |
| 1341 | + var valueIndex = 5; | |
| 1342 | + if(matrixType === "matrix3d") { | |
| 1343 | + valueIndex = 13; | |
| 1344 | + } | |
| 1345 | + | |
| 1346 | + var yTransformation = valuesArray[valueIndex]; | |
| 1347 | + // Need to check to avoid errors when testing and in some other situations. | |
| 1348 | + yTransformation = typeof yTransformation === 'undefined' ? 0 : yTransformation.trim(); | |
| 1349 | + if (yTransformation < 0) { | |
| 1350 | + yTransformation = this.$popupContainer.css("height").replace('px', '') * -1; | |
| 1351 | + valuesArray[valueIndex] = yTransformation; | |
| 1352 | + transformMatrix = matrixType + '(' + valuesArray.join(',') + ')'; | |
| 1353 | + this.$popupContainer.css("transform", transformMatrix); | |
| 1354 | + } | |
| 1355 | + }, | |
| 1356 | + | |
| 1357 | + /** | |
| 1358 | + * Unbinds the whole plugin. | |
| 1359 | + */ | |
| 1360 | + destroy: function () { | |
| 1361 | + this.$container.remove(); | |
| 1362 | + this.$select.unwrap(); | |
| 1363 | + this.$select.show(); | |
| 1364 | + | |
| 1365 | + // reset original state | |
| 1366 | + this.$select.prop('disabled', this.options.wasDisabled); | |
| 1367 | + | |
| 1368 | + this.$select.data('multiselect', null); | |
| 1369 | + }, | |
| 1370 | + | |
| 1371 | + /** | |
| 1372 | + * Refreshs the multiselect based on the selected options of the select. | |
| 1373 | + */ | |
| 1374 | + refresh: function () { | |
| 1375 | + var inputs = {}; | |
| 1376 | + $('.multiselect-option input', this.$popupContainer).each(function () { | |
| 1377 | + inputs[$(this).val()] = $(this); | |
| 1378 | + }); | |
| 1379 | + | |
| 1380 | + $('option', this.$select).each($.proxy(function (index, element) { | |
| 1381 | + var $elem = $(element); | |
| 1382 | + var $input = inputs[$(element).val()]; | |
| 1383 | + | |
| 1384 | + if ($elem.is(':selected')) { | |
| 1385 | + $input.prop('checked', true); | |
| 1386 | + | |
| 1387 | + if (this.options.selectedClass) { | |
| 1388 | + $input.closest('.multiselect-option') | |
| 1389 | + .addClass(this.options.selectedClass); | |
| 1390 | + } | |
| 1391 | + } | |
| 1392 | + else { | |
| 1393 | + $input.prop('checked', false); | |
| 1394 | + | |
| 1395 | + if (this.options.selectedClass) { | |
| 1396 | + $input.closest('.multiselect-option') | |
| 1397 | + .removeClass(this.options.selectedClass); | |
| 1398 | + } | |
| 1399 | + } | |
| 1400 | + | |
| 1401 | + if ($elem.is(":disabled")) { | |
| 1402 | + $input.attr('disabled', 'disabled') | |
| 1403 | + .prop('disabled', true) | |
| 1404 | + .closest('.multiselect-option') | |
| 1405 | + .addClass('disabled'); | |
| 1406 | + } | |
| 1407 | + else { | |
| 1408 | + $input.prop('disabled', false) | |
| 1409 | + .closest('.multiselect-option') | |
| 1410 | + .removeClass('disabled'); | |
| 1411 | + } | |
| 1412 | + }, this)); | |
| 1413 | + | |
| 1414 | + this.updateButtonText(); | |
| 1415 | + this.updateSelectAll(); | |
| 1416 | + | |
| 1417 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1418 | + this.updateOptGroups(); | |
| 1419 | + } | |
| 1420 | + }, | |
| 1421 | + | |
| 1422 | + /** | |
| 1423 | + * Select all options of the given values. | |
| 1424 | + * | |
| 1425 | + * If triggerOnChange is set to true, the on change event is triggered if | |
| 1426 | + * and only if one value is passed. | |
| 1427 | + * | |
| 1428 | + * @param {Array} selectValues | |
| 1429 | + * @param {Boolean} triggerOnChange | |
| 1430 | + */ | |
| 1431 | + select: function (selectValues, triggerOnChange) { | |
| 1432 | + if (!$.isArray(selectValues)) { | |
| 1433 | + selectValues = [selectValues]; | |
| 1434 | + } | |
| 1435 | + | |
| 1436 | + for (var i = 0; i < selectValues.length; i++) { | |
| 1437 | + var value = selectValues[i]; | |
| 1438 | + | |
| 1439 | + if (value === null || value === undefined) { | |
| 1440 | + continue; | |
| 1441 | + } | |
| 1442 | + | |
| 1443 | + var $option = this.getOptionByValue(value); | |
| 1444 | + var $checkbox = this.getInputByValue(value); | |
| 1445 | + | |
| 1446 | + if ($option === undefined || $checkbox === undefined) { | |
| 1447 | + continue; | |
| 1448 | + } | |
| 1449 | + | |
| 1450 | + if (this.options.selectedClass) { | |
| 1451 | + $checkbox.closest('.dropdown-item') | |
| 1452 | + .addClass(this.options.selectedClass); | |
| 1453 | + } | |
| 1454 | + | |
| 1455 | + $checkbox.prop('checked', true); | |
| 1456 | + $option.prop('selected', true); | |
| 1457 | + | |
| 1458 | + if (!this.options.multiple) { | |
| 1459 | + var $checkboxesNotThis = $('input', this.$container).not($checkbox); | |
| 1460 | + $($checkboxesNotThis).prop('checked', false); | |
| 1461 | + $($checkboxesNotThis).closest('.multiselect-option').removeClass("active") | |
| 1462 | + | |
| 1463 | + var $optionsNotThis = $('option', this.$select).not($option); | |
| 1464 | + $optionsNotThis.prop('selected', false); | |
| 1465 | + } | |
| 1466 | + | |
| 1467 | + if (triggerOnChange) { | |
| 1468 | + this.options.onChange($option, true); | |
| 1469 | + } | |
| 1470 | + } | |
| 1471 | + | |
| 1472 | + this.updateButtonText(); | |
| 1473 | + this.updateSelectAll(); | |
| 1474 | + | |
| 1475 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1476 | + this.updateOptGroups(); | |
| 1477 | + } | |
| 1478 | + }, | |
| 1479 | + | |
| 1480 | + /** | |
| 1481 | + * Clears all selected items. | |
| 1482 | + */ | |
| 1483 | + clearSelection: function () { | |
| 1484 | + this.deselectAll(false); | |
| 1485 | + this.updateButtonText(); | |
| 1486 | + this.updateSelectAll(); | |
| 1487 | + | |
| 1488 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1489 | + this.updateOptGroups(); | |
| 1490 | + } | |
| 1491 | + }, | |
| 1492 | + | |
| 1493 | + /** | |
| 1494 | + * Deselects all options of the given values. | |
| 1495 | + * | |
| 1496 | + * If triggerOnChange is set to true, the on change event is triggered, if | |
| 1497 | + * and only if one value is passed. | |
| 1498 | + * | |
| 1499 | + * @param {Array} deselectValues | |
| 1500 | + * @param {Boolean} triggerOnChange | |
| 1501 | + */ | |
| 1502 | + deselect: function (deselectValues, triggerOnChange) { | |
| 1503 | + if (!this.options.multiple) { | |
| 1504 | + // In single selection mode at least on option needs to be selected | |
| 1505 | + return; | |
| 1506 | + } | |
| 1507 | + | |
| 1508 | + if (!$.isArray(deselectValues)) { | |
| 1509 | + deselectValues = [deselectValues]; | |
| 1510 | + } | |
| 1511 | + | |
| 1512 | + for (var i = 0; i < deselectValues.length; i++) { | |
| 1513 | + var value = deselectValues[i]; | |
| 1514 | + | |
| 1515 | + if (value === null || value === undefined) { | |
| 1516 | + continue; | |
| 1517 | + } | |
| 1518 | + | |
| 1519 | + var $option = this.getOptionByValue(value); | |
| 1520 | + var $checkbox = this.getInputByValue(value); | |
| 1521 | + | |
| 1522 | + if ($option === undefined || $checkbox === undefined) { | |
| 1523 | + continue; | |
| 1524 | + } | |
| 1525 | + | |
| 1526 | + if (this.options.selectedClass) { | |
| 1527 | + $checkbox.closest('.dropdown-item') | |
| 1528 | + .removeClass(this.options.selectedClass); | |
| 1529 | + } | |
| 1530 | + | |
| 1531 | + $checkbox.prop('checked', false); | |
| 1532 | + $option.prop('selected', false); | |
| 1533 | + | |
| 1534 | + if (triggerOnChange) { | |
| 1535 | + this.options.onChange($option, false); | |
| 1536 | + } | |
| 1537 | + } | |
| 1538 | + | |
| 1539 | + this.updateButtonText(); | |
| 1540 | + this.updateSelectAll(); | |
| 1541 | + | |
| 1542 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1543 | + this.updateOptGroups(); | |
| 1544 | + } | |
| 1545 | + }, | |
| 1546 | + | |
| 1547 | + /** | |
| 1548 | + * Selects all enabled & visible options. | |
| 1549 | + * | |
| 1550 | + * If justVisible is true or not specified, only visible options are selected. | |
| 1551 | + * | |
| 1552 | + * @param {Boolean} justVisible | |
| 1553 | + * @param {Boolean} triggerOnSelectAll | |
| 1554 | + */ | |
| 1555 | + selectAll: function (justVisible, triggerOnSelectAll) { | |
| 1556 | + if (!this.options.multiple) { | |
| 1557 | + // In single selection mode only one option can be selected at a time | |
| 1558 | + return; | |
| 1559 | + } | |
| 1560 | + | |
| 1561 | + // Record all changes, i.e., options selected that were not selected before. | |
| 1562 | + var selected = []; | |
| 1563 | + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; | |
| 1564 | + | |
| 1565 | + if (justVisible) { | |
| 1566 | + var visibleOptions = $(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)", this.$popupContainer); | |
| 1567 | + $('input:enabled', visibleOptions).prop('checked', true); | |
| 1568 | + visibleOptions.addClass(this.options.selectedClass); | |
| 1569 | + | |
| 1570 | + $('input:enabled', visibleOptions).each($.proxy(function (index, element) { | |
| 1571 | + var value = $(element).val(); | |
| 1572 | + var option = this.getOptionByValue(value); | |
| 1573 | + if (!$(option).prop('selected')) { | |
| 1574 | + selected.push(option); | |
| 1575 | + } | |
| 1576 | + $(option).prop('selected', true); | |
| 1577 | + }, this)); | |
| 1578 | + } | |
| 1579 | + else { | |
| 1580 | + var allOptions = $(".multiselect-option:not(.disabled)", this.$popupContainer); | |
| 1581 | + $('input:enabled', allOptions).prop('checked', true); | |
| 1582 | + allOptions.addClass(this.options.selectedClass); | |
| 1583 | + | |
| 1584 | + $('input:enabled', allOptions).each($.proxy(function (index, element) { | |
| 1585 | + var value = $(element).val(); | |
| 1586 | + var option = this.getOptionByValue(value); | |
| 1587 | + if (!$(option).prop('selected')) { | |
| 1588 | + selected.push(option); | |
| 1589 | + } | |
| 1590 | + $(option).prop('selected', true); | |
| 1591 | + }, this)); | |
| 1592 | + } | |
| 1593 | + | |
| 1594 | + $('.multiselect-option input[value="' + this.options.selectAllValue + '"]', this.$popupContainer).prop('checked', true); | |
| 1595 | + | |
| 1596 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1597 | + this.updateOptGroups(); | |
| 1598 | + } | |
| 1599 | + | |
| 1600 | + this.updateButtonText(); | |
| 1601 | + this.updateSelectAll(); | |
| 1602 | + | |
| 1603 | + if (triggerOnSelectAll) { | |
| 1604 | + this.options.onSelectAll(selected); | |
| 1605 | + } | |
| 1606 | + }, | |
| 1607 | + | |
| 1608 | + /** | |
| 1609 | + * Deselects all options. | |
| 1610 | + * | |
| 1611 | + * If justVisible is true or not specified, only visible options are deselected. | |
| 1612 | + * | |
| 1613 | + * @param {Boolean} justVisible | |
| 1614 | + */ | |
| 1615 | + deselectAll: function (justVisible, triggerOnDeselectAll) { | |
| 1616 | + if (!this.options.multiple) { | |
| 1617 | + // In single selection mode at least on option needs to be selected | |
| 1618 | + return; | |
| 1619 | + } | |
| 1620 | + | |
| 1621 | + // Record changes, i.e., those options that are deselected but were not deselected before. | |
| 1622 | + var deselected = []; | |
| 1623 | + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; | |
| 1624 | + | |
| 1625 | + if (justVisible) { | |
| 1626 | + var visibleOptions = $(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)", this.$popupContainer); | |
| 1627 | + $('input[type="checkbox"]:enabled', visibleOptions).prop('checked', false); | |
| 1628 | + visibleOptions.removeClass(this.options.selectedClass); | |
| 1629 | + | |
| 1630 | + $('input[type="checkbox"]:enabled', visibleOptions).each($.proxy(function (index, element) { | |
| 1631 | + var value = $(element).val(); | |
| 1632 | + var option = this.getOptionByValue(value); | |
| 1633 | + if ($(option).prop('selected')) { | |
| 1634 | + deselected.push(option); | |
| 1635 | + } | |
| 1636 | + $(option).prop('selected', false); | |
| 1637 | + }, this)); | |
| 1638 | + } | |
| 1639 | + else { | |
| 1640 | + var allOptions = $(".multiselect-option:not(.disabled):not(.multiselect-group)", this.$popupContainer); | |
| 1641 | + $('input[type="checkbox"]:enabled', allOptions).prop('checked', false); | |
| 1642 | + allOptions.removeClass(this.options.selectedClass); | |
| 1643 | + | |
| 1644 | + $('input[type="checkbox"]:enabled', allOptions).each($.proxy(function (index, element) { | |
| 1645 | + var value = $(element).val(); | |
| 1646 | + var option = this.getOptionByValue(value); | |
| 1647 | + if ($(option).prop('selected')) { | |
| 1648 | + deselected.push(option); | |
| 1649 | + } | |
| 1650 | + $(option).prop('selected', false); | |
| 1651 | + }, this)); | |
| 1652 | + } | |
| 1653 | + | |
| 1654 | + $('.multiselect-all input[value="' + this.options.selectAllValue + '"]', this.$popupContainer).prop('checked', false); | |
| 1655 | + | |
| 1656 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1657 | + this.updateOptGroups(); | |
| 1658 | + } | |
| 1659 | + | |
| 1660 | + this.updateButtonText(); | |
| 1661 | + this.updateSelectAll(); | |
| 1662 | + | |
| 1663 | + if (triggerOnDeselectAll) { | |
| 1664 | + this.options.onDeselectAll(deselected); | |
| 1665 | + } | |
| 1666 | + }, | |
| 1667 | + | |
| 1668 | + /** | |
| 1669 | + * Rebuild the plugin. | |
| 1670 | + * | |
| 1671 | + * Rebuilds the dropdown, the filter and the select all option. | |
| 1672 | + */ | |
| 1673 | + rebuild: function () { | |
| 1674 | + this.$popupContainer.html(''); | |
| 1675 | + | |
| 1676 | + // Important to distinguish between radios and checkboxes. | |
| 1677 | + this.options.multiple = this.$select.attr('multiple') === "multiple"; | |
| 1678 | + | |
| 1679 | + this.buildSelectAll(); | |
| 1680 | + this.buildDropdownOptions(); | |
| 1681 | + this.buildFilter(); | |
| 1682 | + this.buildButtons(); | |
| 1683 | + | |
| 1684 | + this.updateButtonText(); | |
| 1685 | + this.updateSelectAll(true); | |
| 1686 | + | |
| 1687 | + if (this.options.enableClickableOptGroups && this.options.multiple) { | |
| 1688 | + this.updateOptGroups(); | |
| 1689 | + } | |
| 1690 | + | |
| 1691 | + if (this.options.disableIfEmpty) { | |
| 1692 | + if ($('option', this.$select).length <= 0) { | |
| 1693 | + if (!this.$select.prop('disabled')) { | |
| 1694 | + this.disable(true); | |
| 1695 | + } | |
| 1696 | + } | |
| 1697 | + else if (this.$select.data("disabled-by-option")) { | |
| 1698 | + this.enable(); | |
| 1699 | + } | |
| 1700 | + } | |
| 1701 | + | |
| 1702 | + if (this.options.dropRight) { | |
| 1703 | + this.$container.addClass('dropright'); | |
| 1704 | + } | |
| 1705 | + else if (this.options.dropUp) { | |
| 1706 | + this.$container.addClass('dropup'); | |
| 1707 | + } | |
| 1708 | + | |
| 1709 | + if (this.options.widthSynchronizationMode !== 'never') { | |
| 1710 | + this.synchronizeButtonAndPopupWidth(); | |
| 1711 | + } | |
| 1712 | + }, | |
| 1713 | + | |
| 1714 | + /** | |
| 1715 | + * The provided data will be used to build the dropdown. | |
| 1716 | + */ | |
| 1717 | + dataprovider: function (dataprovider) { | |
| 1718 | + | |
| 1719 | + var groupCounter = 0; | |
| 1720 | + var $select = this.$select.empty(); | |
| 1721 | + | |
| 1722 | + $.each(dataprovider, function (index, option) { | |
| 1723 | + var $tag; | |
| 1724 | + | |
| 1725 | + if ($.isArray(option.children)) { // create optiongroup tag | |
| 1726 | + groupCounter++; | |
| 1727 | + | |
| 1728 | + $tag = $('<optgroup/>').attr({ | |
| 1729 | + label: option.label || 'Group ' + groupCounter, | |
| 1730 | + disabled: !!option.disabled, | |
| 1731 | + value: option.value | |
| 1732 | + }); | |
| 1733 | + | |
| 1734 | + forEach(option.children, function (subOption) { // add children option tags | |
| 1735 | + var attributes = { | |
| 1736 | + value: subOption.value, | |
| 1737 | + label: subOption.label || subOption.value, | |
| 1738 | + title: subOption.title, | |
| 1739 | + selected: !!subOption.selected, | |
| 1740 | + disabled: !!subOption.disabled | |
| 1741 | + }; | |
| 1742 | + | |
| 1743 | + //Loop through attributes object and add key-value for each attribute | |
| 1744 | + for (var key in subOption.attributes) { | |
| 1745 | + attributes['data-' + key] = subOption.attributes[key]; | |
| 1746 | + } | |
| 1747 | + //Append original attributes + new data attributes to option | |
| 1748 | + $tag.append($('<option/>').attr(attributes)); | |
| 1749 | + }); | |
| 1750 | + } | |
| 1751 | + else { | |
| 1752 | + | |
| 1753 | + var attributes = { | |
| 1754 | + 'value': option.value, | |
| 1755 | + 'label': option.label || option.value, | |
| 1756 | + 'title': option.title, | |
| 1757 | + 'class': option['class'], | |
| 1758 | + 'selected': !!option['selected'], | |
| 1759 | + 'disabled': !!option['disabled'] | |
| 1760 | + }; | |
| 1761 | + //Loop through attributes object and add key-value for each attribute | |
| 1762 | + for (var key in option.attributes) { | |
| 1763 | + attributes['data-' + key] = option.attributes[key]; | |
| 1764 | + } | |
| 1765 | + //Append original attributes + new data attributes to option | |
| 1766 | + $tag = $('<option/>').attr(attributes); | |
| 1767 | + | |
| 1768 | + $tag.text(option.label || option.value); | |
| 1769 | + } | |
| 1770 | + | |
| 1771 | + $select.append($tag); | |
| 1772 | + }); | |
| 1773 | + | |
| 1774 | + this.rebuild(); | |
| 1775 | + }, | |
| 1776 | + | |
| 1777 | + /** | |
| 1778 | + * Enable the multiselect. | |
| 1779 | + */ | |
| 1780 | + enable: function () { | |
| 1781 | + this.$select.prop('disabled', false); | |
| 1782 | + this.$button.prop('disabled', false) | |
| 1783 | + .removeClass('disabled'); | |
| 1784 | + | |
| 1785 | + this.updateButtonText(); | |
| 1786 | + }, | |
| 1787 | + | |
| 1788 | + /** | |
| 1789 | + * Disable the multiselect. | |
| 1790 | + */ | |
| 1791 | + disable: function (disableByOption) { | |
| 1792 | + this.$select.prop('disabled', true); | |
| 1793 | + this.$button.prop('disabled', true) | |
| 1794 | + .addClass('disabled'); | |
| 1795 | + | |
| 1796 | + if (disableByOption) { | |
| 1797 | + this.$select.data("disabled-by-option", true); | |
| 1798 | + } | |
| 1799 | + else { | |
| 1800 | + this.$select.data("disabled-by-option", null); | |
| 1801 | + } | |
| 1802 | + | |
| 1803 | + this.updateButtonText(); | |
| 1804 | + }, | |
| 1805 | + | |
| 1806 | + /** | |
| 1807 | + * Set the options. | |
| 1808 | + * | |
| 1809 | + * @param {Array} options | |
| 1810 | + */ | |
| 1811 | + setOptions: function (options) { | |
| 1812 | + this.options = this.mergeOptions(options); | |
| 1813 | + }, | |
| 1814 | + | |
| 1815 | + /** | |
| 1816 | + * Merges the given options with the default options. | |
| 1817 | + * | |
| 1818 | + * @param {Array} options | |
| 1819 | + * @returns {Array} | |
| 1820 | + */ | |
| 1821 | + mergeOptions: function (options) { | |
| 1822 | + return $.extend(true, {}, this.defaults, this.options, options); | |
| 1823 | + }, | |
| 1824 | + | |
| 1825 | + /** | |
| 1826 | + * Checks whether a select all checkbox is present. | |
| 1827 | + * | |
| 1828 | + * @returns {Boolean} | |
| 1829 | + */ | |
| 1830 | + hasSelectAll: function () { | |
| 1831 | + return $('.multiselect-all', this.$popupContainer).length > 0; | |
| 1832 | + }, | |
| 1833 | + | |
| 1834 | + /** | |
| 1835 | + * Update opt groups. | |
| 1836 | + */ | |
| 1837 | + updateOptGroups: function () { | |
| 1838 | + var $groups = $('.multiselect-group', this.$popupContainer) | |
| 1839 | + var selectedClass = this.options.selectedClass; | |
| 1840 | + | |
| 1841 | + $groups.each(function () { | |
| 1842 | + var $options = $(this).nextUntil('.multiselect-group') | |
| 1843 | + .not('.multiselect-filter-hidden') | |
| 1844 | + .not('.disabled'); | |
| 1845 | + | |
| 1846 | + var checked = true; | |
| 1847 | + $options.each(function () { | |
| 1848 | + var $input = $('input', this); | |
| 1849 | + | |
| 1850 | + if (!$input.prop('checked')) { | |
| 1851 | + checked = false; | |
| 1852 | + } | |
| 1853 | + }); | |
| 1854 | + | |
| 1855 | + if (selectedClass) { | |
| 1856 | + if (checked) { | |
| 1857 | + $(this).addClass(selectedClass); | |
| 1858 | + } | |
| 1859 | + else { | |
| 1860 | + $(this).removeClass(selectedClass); | |
| 1861 | + } | |
| 1862 | + } | |
| 1863 | + | |
| 1864 | + $('input', this).prop('checked', checked); | |
| 1865 | + }); | |
| 1866 | + }, | |
| 1867 | + | |
| 1868 | + /** | |
| 1869 | + * Updates the select all checkbox based on the currently displayed and selected checkboxes. | |
| 1870 | + */ | |
| 1871 | + updateSelectAll: function (notTriggerOnSelectAll) { | |
| 1872 | + if (this.hasSelectAll()) { | |
| 1873 | + var allBoxes = $(".multiselect-option:not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$popupContainer); | |
| 1874 | + var allBoxesLength = allBoxes.length; | |
| 1875 | + var checkedBoxesLength = allBoxes.filter(":checked").length; | |
| 1876 | + var selectAllItem = $(".multiselect-all", this.$popupContainer); | |
| 1877 | + var selectAllInput = selectAllItem.find("input"); | |
| 1878 | + | |
| 1879 | + if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) { | |
| 1880 | + selectAllInput.prop("checked", true); | |
| 1881 | + selectAllItem.addClass(this.options.selectedClass); | |
| 1882 | + } | |
| 1883 | + else { | |
| 1884 | + selectAllInput.prop("checked", false); | |
| 1885 | + selectAllItem.removeClass(this.options.selectedClass); | |
| 1886 | + } | |
| 1887 | + } | |
| 1888 | + }, | |
| 1889 | + | |
| 1890 | + /** | |
| 1891 | + * Update the button text and its title based on the currently selected options. | |
| 1892 | + */ | |
| 1893 | + updateButtonText: function () { | |
| 1894 | + var options = this.getSelected(); | |
| 1895 | + | |
| 1896 | + // First update the displayed button text. | |
| 1897 | + if (this.options.enableHTML) { | |
| 1898 | + $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select)); | |
| 1899 | + } | |
| 1900 | + else { | |
| 1901 | + $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select)); | |
| 1902 | + } | |
| 1903 | + | |
| 1904 | + // Now update the title attribute of the button. | |
| 1905 | + $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select)); | |
| 1906 | + this.$button.trigger('change'); | |
| 1907 | + }, | |
| 1908 | + | |
| 1909 | + /** | |
| 1910 | + * Get all selected options. | |
| 1911 | + * | |
| 1912 | + * @returns {jQUery} | |
| 1913 | + */ | |
| 1914 | + getSelected: function () { | |
| 1915 | + return $('option', this.$select).filter(":selected"); | |
| 1916 | + }, | |
| 1917 | + | |
| 1918 | + /** | |
| 1919 | + * Gets a select option by its value. | |
| 1920 | + * | |
| 1921 | + * @param {String} value | |
| 1922 | + * @returns {jQuery} | |
| 1923 | + */ | |
| 1924 | + getOptionByValue: function (value) { | |
| 1925 | + | |
| 1926 | + var options = $('option', this.$select); | |
| 1927 | + var valueToCompare = value.toString(); | |
| 1928 | + | |
| 1929 | + for (var i = 0; i < options.length; i = i + 1) { | |
| 1930 | + var option = options[i]; | |
| 1931 | + if (option.value === valueToCompare) { | |
| 1932 | + return $(option); | |
| 1933 | + } | |
| 1934 | + } | |
| 1935 | + }, | |
| 1936 | + | |
| 1937 | + /** | |
| 1938 | + * Get the input (radio/checkbox) by its value. | |
| 1939 | + * | |
| 1940 | + * @param {String} value | |
| 1941 | + * @returns {jQuery} | |
| 1942 | + */ | |
| 1943 | + getInputByValue: function (value) { | |
| 1944 | + | |
| 1945 | + var checkboxes = $('.multiselect-option input:not(.multiselect-search)', this.$popupContainer); | |
| 1946 | + var valueToCompare = value.toString(); | |
| 1947 | + | |
| 1948 | + for (var i = 0; i < checkboxes.length; i = i + 1) { | |
| 1949 | + var checkbox = checkboxes[i]; | |
| 1950 | + if (checkbox.value === valueToCompare) { | |
| 1951 | + return $(checkbox); | |
| 1952 | + } | |
| 1953 | + } | |
| 1954 | + }, | |
| 1955 | + | |
| 1956 | + /** | |
| 1957 | + * Used for knockout integration. | |
| 1958 | + */ | |
| 1959 | + updateOriginalOptions: function () { | |
| 1960 | + this.originalOptions = this.$select.clone()[0].options; | |
| 1961 | + }, | |
| 1962 | + | |
| 1963 | + asyncFunction: function (callback, timeout, self) { | |
| 1964 | + var args = Array.prototype.slice.call(arguments, 3); | |
| 1965 | + return setTimeout(function () { | |
| 1966 | + callback.apply(self || window, args); | |
| 1967 | + }, timeout); | |
| 1968 | + }, | |
| 1969 | + | |
| 1970 | + setAllSelectedText: function (allSelectedText) { | |
| 1971 | + this.options.allSelectedText = allSelectedText; | |
| 1972 | + this.updateButtonText(); | |
| 1973 | + }, | |
| 1974 | + | |
| 1975 | + isFirefox: function () { | |
| 1976 | + var firefoxIdentifier = 'firefox'; | |
| 1977 | + var valueNotFoundIndex = -1; | |
| 1978 | + | |
| 1979 | + if (navigator && navigator.userAgent) { | |
| 1980 | + return navigator.userAgent.toLocaleLowerCase().indexOf(firefoxIdentifier) > valueNotFoundIndex; | |
| 1981 | + } | |
| 1982 | + | |
| 1983 | + return false; | |
| 1984 | + } | |
| 1985 | + }; | |
| 1986 | + | |
| 1987 | + $.fn.multiselect = function (option, parameter, extraOptions) { | |
| 1988 | + return this.each(function () { | |
| 1989 | + var data = $(this).data('multiselect'); | |
| 1990 | + var options = typeof option === 'object' && option; | |
| 1991 | + | |
| 1992 | + // Initialize the multiselect. | |
| 1993 | + if (!data) { | |
| 1994 | + data = new Multiselect(this, options); | |
| 1995 | + $(this).data('multiselect', data); | |
| 1996 | + } | |
| 1997 | + | |
| 1998 | + // Call multiselect method. | |
| 1999 | + if (typeof option === 'string') { | |
| 2000 | + data[option](parameter, extraOptions); | |
| 2001 | + | |
| 2002 | + if (option === 'destroy') { | |
| 2003 | + $(this).data('multiselect', false); | |
| 2004 | + } | |
| 2005 | + } | |
| 2006 | + }); | |
| 2007 | + }; | |
| 2008 | + | |
| 2009 | + $.fn.multiselect.Constructor = Multiselect; | |
| 2010 | + | |
| 2011 | + $(function () { | |
| 2012 | + $("select[data-role=multiselect]").multiselect(); | |
| 2013 | + }); | |
| 2014 | + | |
| 2015 | +}); | |