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