|
49 | 49 | groupColumns: false
|
50 | 50 | },
|
51 | 51 |
|
| 52 | + // This method determines which element to append the menu to |
| 53 | + // Uses the element provided in the options first, then looks for ui-front / dialog |
| 54 | + // Otherwise appends to the body |
52 | 55 | _getAppendEl: function() {
|
53 | 56 | var element = this.options.appendTo;
|
54 | 57 | if(element) {
|
|
63 | 66 | return element;
|
64 | 67 | },
|
65 | 68 |
|
| 69 | + // Performs the initial creation of the widget |
66 | 70 | _create: function() {
|
67 | 71 | var el = this.element;
|
68 | 72 | var o = this.options;
|
69 | 73 |
|
70 | 74 | this.speed = $.fx.speeds._default; // default speed for effects
|
71 | 75 | this._isOpen = false; // assume no
|
72 |
| - this.inputIdCounter = 0; |
| 76 | + this.inputIdCounter = 0; // Incremented for each input item (option) |
73 | 77 |
|
74 | 78 | // create a unique namespace for events that the widget
|
75 | 79 | // factory cannot unbind automatically. Use eventNamespace if on
|
|
78 | 82 | // bump unique ID after assigning it to the widget instance
|
79 | 83 | this.multiselectID = multiselectID++;
|
80 | 84 |
|
| 85 | + // The button that opens the widget menu |
81 | 86 | var button = (this.button = $('<button type="button"><span class="ui-icon ui-icon-triangle-1-s"></span></button>'))
|
82 | 87 | .addClass('ui-multiselect ui-widget ui-state-default ui-corner-all ' + o.classes)
|
83 | 88 | .attr({ 'title':el.attr('title'), 'tabIndex':el.attr('tabIndex'), 'id': el.attr('id') ? el.attr('id') + '_ms' : null })
|
84 | 89 | .prop('aria-haspopup', true)
|
85 | 90 | .insertAfter(el);
|
86 | 91 |
|
87 |
| - this.buttonlabel = $('<span />') |
88 |
| - .html(o.noneSelectedText) |
89 |
| - .appendTo(button); |
90 |
| - |
91 |
| - this.menu = $('<div />') |
92 |
| - .addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all ' + o.classes) |
93 |
| - .appendTo(this._getAppendEl()); |
94 |
| - |
95 |
| - this.header = $('<div />') |
96 |
| - .addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix') |
97 |
| - .appendTo(this.menu); |
98 |
| - |
99 |
| - this.headerLinkContainer = $('<ul />') |
100 |
| - .addClass('ui-helper-reset') |
101 |
| - .html(function() { |
102 |
| - if(o.header === true) { |
103 |
| - var header_lis = ''; |
104 |
| - if(o.showCheckAll) { |
105 |
| - header_lis = '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li>'; |
106 |
| - } |
107 |
| - if(o.showUncheckAll) { |
108 |
| - header_lis += '<li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>'; |
109 |
| - } |
110 |
| - return header_lis; |
111 |
| - } else if(typeof o.header === "string") { |
112 |
| - return '<li>' + o.header + '</li>'; |
113 |
| - } else { |
114 |
| - return ''; |
| 92 | + this.buttonlabel = $('<span />') |
| 93 | + .html(o.noneSelectedText) |
| 94 | + .appendTo(button); |
| 95 | + |
| 96 | + // This is the menu that will hold all the options |
| 97 | + this.menu = $('<div />') |
| 98 | + .addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all ' + o.classes) |
| 99 | + .appendTo(this._getAppendEl()); |
| 100 | + |
| 101 | + // Menu header to hold controls for the menu |
| 102 | + this.header = $('<div />') |
| 103 | + .addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix') |
| 104 | + .appendTo(this.menu); |
| 105 | + |
| 106 | + // Header controls, will contain the check all/uncheck all buttons |
| 107 | + // Depending on how the options are set, this may be empty or simply plain text |
| 108 | + this.headerLinkContainer = $('<ul />') |
| 109 | + .addClass('ui-helper-reset') |
| 110 | + .html(function() { |
| 111 | + if(o.header === true) { |
| 112 | + var header_lis = ''; |
| 113 | + if(o.showCheckAll) { |
| 114 | + header_lis = '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li>'; |
| 115 | + } |
| 116 | + if(o.showUncheckAll) { |
| 117 | + header_lis += '<li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>'; |
115 | 118 | }
|
116 |
| - }) |
117 |
| - .append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon '+o.closeIcon+'"></span></a></li>') |
118 |
| - .appendTo(this.header); |
| 119 | + return header_lis; |
| 120 | + } else if(typeof o.header === "string") { |
| 121 | + return '<li>' + o.header + '</li>'; |
| 122 | + } else { |
| 123 | + return ''; |
| 124 | + } |
| 125 | + }) |
| 126 | + .append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon '+o.closeIcon+'"></span></a></li>') |
| 127 | + .appendTo(this.header); |
119 | 128 |
|
120 |
| - var checkboxContainer = (this.checkboxContainer = $('<ul />')) |
121 |
| - .addClass('ui-multiselect-checkboxes ui-helper-reset') |
122 |
| - .appendTo(this.menu); |
| 129 | + // Holds the actual check boxes for inputs |
| 130 | + var checkboxContainer = (this.checkboxContainer = $('<ul />')) |
| 131 | + .addClass('ui-multiselect-checkboxes ui-helper-reset') |
| 132 | + .appendTo(this.menu); |
123 | 133 |
|
124 |
| - // perform event bindings |
125 |
| - this._bindEvents(); |
| 134 | + this._bindEvents(); |
126 | 135 |
|
127 |
| - // build menu |
128 |
| - this.refresh(true); |
| 136 | + // build menu |
| 137 | + this.refresh(true); |
129 | 138 |
|
130 |
| - // some addl. logic for single selects |
131 |
| - if(!o.multiple) { |
132 |
| - this.menu.addClass('ui-multiselect-single'); |
133 |
| - } |
134 |
| - el.hide(); |
| 139 | + // If this is a single select widget, add the appropriate class |
| 140 | + if(!o.multiple) { |
| 141 | + this.menu.addClass('ui-multiselect-single'); |
| 142 | + } |
| 143 | + el.hide(); |
135 | 144 | },
|
136 | 145 |
|
| 146 | + // https://api.jqueryui.com/jquery.widget/#method-_init |
137 | 147 | _init: function() {
|
138 | 148 | if(this.options.header === false) {
|
139 | 149 | this.header.hide();
|
|
151 | 161 | }
|
152 | 162 | },
|
153 | 163 |
|
| 164 | + /* |
| 165 | + * Builds an option item for the menu |
| 166 | + * <li> |
| 167 | + * <label> |
| 168 | + * <input /> checkbox or radio depending on single/multiple select |
| 169 | + * <span /> option text |
| 170 | + * </label> |
| 171 | + * </li> |
| 172 | + */ |
154 | 173 | _makeOption: function(option) {
|
155 | 174 | var title = option.title ? option.title : null;
|
156 | 175 | var value = option.value;
|
|
198 | 217 |
|
199 | 218 | return $item;
|
200 | 219 | },
|
201 |
| - |
| 220 | + // Builds a menu item for each option in the underlying select |
| 221 | + // Option groups are built here as well |
202 | 222 | _buildOptionList: function(element, $appendTo) {
|
203 | 223 | var self = this;
|
204 | 224 | element.children().each(function() {
|
|
217 | 237 |
|
218 | 238 | },
|
219 | 239 |
|
| 240 | + // Refreshes the widget to pick up changes to the underlying select |
| 241 | + // Rebuilds the menu, sets button width |
220 | 242 | refresh: function(init) {
|
221 | 243 | var self = this;
|
222 | 244 | var el = this.element;
|
223 | 245 | var o = this.options;
|
224 | 246 | var menu = this.menu;
|
225 | 247 | var checkboxContainer = this.checkboxContainer;
|
226 |
| - var html = ""; |
227 | 248 | var $dropdown = $("<ul/>").addClass('ui-multiselect-checkboxes ui-helper-reset');
|
228 | 249 | this.inputIdCounter = 0;
|
229 | 250 |
|
|
284 | 305 | },
|
285 | 306 |
|
286 | 307 | // this exists as a separate method so that the developer
|
287 |
| - // can easily override it. |
| 308 | + // can easily override it, usually to allow injecting HTML if they really want it |
288 | 309 | _setButtonValue: function(value) {
|
289 | 310 | this.buttonlabel.text(value);
|
290 | 311 | },
|
|
398 | 419 | self._traverse(e.which, this);
|
399 | 420 | break;
|
400 | 421 | case 13: // enter
|
401 |
| - case 32: |
| 422 | + case 32: //space |
402 | 423 | $(this).find('input')[0].click();
|
403 | 424 | break;
|
404 |
| - case 65: |
| 425 | + case 65: // a |
405 | 426 | if(e.altKey) {
|
406 | 427 | self.checkAll();
|
407 | 428 | }
|
408 | 429 | break;
|
409 |
| - case 85: |
| 430 | + case 85: // u |
410 | 431 | if(e.altKey) {
|
411 | 432 | self.uncheckAll();
|
412 | 433 | }
|
|
475 | 496 | e.preventDefault();
|
476 | 497 | }).on('keydown.multiselect', 'a', function(e) {
|
477 | 498 | switch(e.which) {
|
478 |
| - case 27: |
| 499 | + case 27: // esc |
479 | 500 | self.close();
|
480 | 501 | break;
|
481 |
| - case 9: |
| 502 | + case 9: // tab |
482 | 503 | var $target = $(e.target);
|
483 | 504 | if((e.shiftKey && !$target.parent().prev().length && !self.header.find(".ui-multiselect-filter").length) || (!$target.parent().next().length && !self.labels.length && !e.shiftKey)) {
|
484 | 505 | self.close();
|
|
489 | 510 | });
|
490 | 511 | },
|
491 | 512 |
|
492 |
| - // binds events |
493 | 513 | _bindEvents: function() {
|
494 | 514 | var self = this;
|
495 | 515 |
|
|
519 | 539 | setTimeout($.proxy(self.refresh, self), 10);
|
520 | 540 | });
|
521 | 541 | },
|
| 542 | + |
| 543 | + // Determines the minimum width for the button and menu |
| 544 | + // Can be a number, a digit string, or a percentage |
522 | 545 | _getMinWidth: function() {
|
523 | 546 | var minVal = this.options.minWidth;
|
524 | 547 | var width = 0;
|
|
557 | 580 | m.outerWidth(this.options.menuWidth || width);
|
558 | 581 | },
|
559 | 582 |
|
| 583 | + // Sets the height of the menu |
| 584 | + // Will set a scroll bar if the menu height exceeds that of the height in options |
560 | 585 | _setMenuHeight: function() {
|
561 | 586 | var headerHeight = this.menu.children(".ui-multiselect-header:visible").outerHeight(true);
|
562 | 587 | var ulHeight = 0;
|
|
574 | 599 | this.menu.height(ulHeight + headerHeight);
|
575 | 600 | },
|
576 | 601 |
|
| 602 | + // Resizes the menu, called every time the menu is opened |
577 | 603 | _resizeMenu: function() {
|
578 | 604 | this._setMenuWidth();
|
579 | 605 | this._setMenuHeight();
|
|
624 | 650 | };
|
625 | 651 | },
|
626 | 652 |
|
| 653 | + // Toggles checked state on either an option group or all inputs |
627 | 654 | _toggleChecked: function(flag, group) {
|
628 | 655 | var $inputs = (group && group.length) ? group : this.inputs;
|
629 | 656 | var self = this;
|
|
658 | 685 | }
|
659 | 686 | },
|
660 | 687 |
|
| 688 | + // Toggle disable state on the widget and underlying select |
661 | 689 | _toggleDisabled: function(flag) {
|
662 | 690 | this.button.prop({ 'disabled':flag, 'aria-disabled':flag })[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled');
|
663 | 691 |
|
|
840 | 868 | return this.labels;
|
841 | 869 | },
|
842 | 870 |
|
| 871 | + /* |
| 872 | + * Adds an option to the widget and underlying select |
| 873 | + * attributes: Attributes hash to add to the option |
| 874 | + * text: text of the option |
| 875 | + * groupLabel: Option Group to add the option to |
| 876 | + */ |
843 | 877 | addOption: function(attributes, text, groupLabel) {
|
844 | 878 | var $option = $("<option/>").attr(attributes).text(text);
|
845 | 879 | var optionNode = $option.get(0);
|
|
0 commit comments