1/* 2 * jQuery MultiSelect UI Widget Filtering Plugin 1.5pre 3 * Dual licensed under the MIT and GPL licenses: 4 * http://www.opensource.org/licenses/mit-license.php 5 * http://www.gnu.org/licenses/gpl.html 6 */ 7(function($) { 8 var rEscape = /[\-\[\]{}()*+?.,\\\^$|#\s]/g; 9 10 $.widget('ech.multiselectfilter', { 11 12 options: { 13 label: 'Filter:', 14 width: null, /* override default width set in css file (px). null will inherit */ 15 placeholder: 'Enter keywords', 16 autoReset: false 17 }, 18 19 _create: function() { 20 var opts = this.options; 21 var elem = $(this.element); 22 23 // get the multiselect instance 24 var instance = (this.instance = (elem.data('echMultiselect') || elem.data("multiselect") || elem.data("ech-multiselect"))); 25 26 // store header; add filter class so the close/check all/uncheck all links can be positioned correctly 27 var header = (this.header = instance.menu.find('.ui-multiselect-header').addClass('ui-multiselect-hasfilter')); 28 29 // wrapper elem 30 var wrapper = (this.wrapper = $('<div class="ui-multiselect-filter">' + (opts.label.length ? opts.label : '') + '<input placeholder="'+opts.placeholder+'" type="search"' + (/\d/.test(opts.width) ? 'style="width:'+opts.width+'px"' : '') + ' /></div>').prependTo(this.header)); 31 32 // reference to the actual inputs 33 this.inputs = instance.menu.find('input[type="checkbox"], input[type="radio"]'); 34 35 // build the input box 36 this.input = wrapper.find('input').bind({ 37 keydown: function(e) { 38 // prevent the enter key from submitting the form / closing the widget 39 if(e.which === 13) { 40 e.preventDefault(); 41 } 42 }, 43 keyup: $.proxy(this._handler, this), 44 click: $.proxy(this._handler, this) 45 }); 46 47 // cache input values for searching 48 this.updateCache(); 49 50 // rewrite internal _toggleChecked fn so that when checkAll/uncheckAll is fired, 51 // only the currently filtered elements are checked 52 instance._toggleChecked = function(flag, group) { 53 var $inputs = (group && group.length) ? group : this.labels.find('input'); 54 var _self = this; 55 56 // do not include hidden elems if the menu isn't open. 57 var selector = instance._isOpen ? ':disabled, :hidden' : ':disabled'; 58 59 $inputs = $inputs 60 .not(selector) 61 .each(this._toggleState('checked', flag)); 62 63 // update text 64 this.update(); 65 66 // gather an array of the values that actually changed 67 var values = $inputs.map(function() { 68 return this.value; 69 }).get(); 70 71 // select option tags 72 this.element.find('option').filter(function() { 73 if(!this.disabled && $.inArray(this.value, values) > -1) { 74 _self._toggleState('selected', flag).call(this); 75 } 76 }); 77 78 // trigger the change event on the select 79 if($inputs.length) { 80 this.element.trigger('change'); 81 } 82 }; 83 84 // rebuild cache when multiselect is updated 85 var doc = $(document).bind('multiselectrefresh', $.proxy(function() { 86 this.updateCache(); 87 this._handler(); 88 }, this)); 89 90 // automatically reset the widget on close? 91 if(this.options.autoReset) { 92 doc.bind('multiselectclose', $.proxy(this._reset, this)); 93 } 94 }, 95 96 // thx for the logic here ben alman 97 _handler: function(e) { 98 var term = $.trim(this.input[0].value.toLowerCase()), 99 100 // speed up lookups 101 rows = this.rows, inputs = this.inputs, cache = this.cache; 102 103 rows.hide(); 104 105 // filter by multiple tokens 106 var terms = term.split(/\s+/g), regexes = []; 107 for (var i = 0; i < terms.length; i++) { 108 regexes.push(new RegExp(terms[i].replace(rEscape, "\\$&"), 'gi')); 109 } 110 111 this._trigger("filter", e, $.map(cache, function(v, i) { 112 var found = true; 113 for (var r = 0; r < regexes.length; r++) { 114 found &= v.search(regexes[r]) !== -1; 115 } 116 if(found) { 117 var entry = rows.eq(i); 118 var label = entry.children();
119 var chk = inputs.eq(i).detach(); 120 var txt = label.text(); 121 label.html('<span>' + txt + '</span>').prepend(chk); 122 entry.show(); 123 return chk.get(0); 124 } 125 126 return null; 127 })); 128 129 // show/hide optgroups 130 this.instance.menu.find(".ui-multiselect-optgroup-label").each(function() { 131 var $this = $(this); 132 var isVisible = $this.nextUntil('.ui-multiselect-optgroup-label').filter(function() { 133 return $.css(this, "display") !== 'none'; 134 }).length; 135 136 $this[isVisible ? 'show' : 'hide'](); 137 }); 138 }, 139 140 _reset: function() { 141 this.input.val('').trigger('keyup'); 142 }, 143 144 updateCache: function() { 145 // each list item 146 this.rows = this.instance.menu.find(".ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label)"); 147 148 // cache 149 this.cache = this.element.children().map(function() { 150 var elem = $(this); 151 152 // account for optgroups 153 if(this.tagName.toLowerCase() === "optgroup") { 154 elem = elem.children(); 155 } 156 157 return elem.map(function() { 158 return this.innerHTML.toLowerCase(); 159 }).get(); 160 }).get(); 161 }, 162 163 widget: function() { 164 return this.wrapper; 165 }, 166 167 destroy: function() { 168 $.Widget.prototype.destroy.call(this); 169 this.input.val('').trigger("keyup"); 170 this.wrapper.remove(); 171 } 172 }); 173 174})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.