1 | /* |
---|
2 | // jQuery multiSelect |
---|
3 | // |
---|
4 | // Version 1.0 beta |
---|
5 | // |
---|
6 | // Cory S.N. LaViska |
---|
7 | // A Beautiful Site (http://abeautifulsite.net/) |
---|
8 | // 06 April 2008 |
---|
9 | // |
---|
10 | // Visit http://abeautifulsite.net/notebook.php?article=62 for more information |
---|
11 | // |
---|
12 | // Usage: $('#control_id').multiSelect( options, callback ) |
---|
13 | // |
---|
14 | // Options: selectAll - whether or not to display the Select All option; true/false, default = true |
---|
15 | // selectAllText - text to display for selecting/unselecting all options simultaneously |
---|
16 | // noneSelected - text to display when there are no selected items in the list |
---|
17 | // oneOrMoreSelected - text to display when there are one or more selected items in the list |
---|
18 | // (note: you can use % as a placeholder for the number of items selected). |
---|
19 | // Use * to show a comma separated list of all selected; default = '% selected' |
---|
20 | // |
---|
21 | // Dependencies: jQuery 1.2 or higher (http://jquery.com/) |
---|
22 | // the jQuery Dimensions plugin (http://plugins.jquery.com/project/dimensions) |
---|
23 | // |
---|
24 | // Licensing & Terms of Use |
---|
25 | // |
---|
26 | // jQuery File Tree is licensed under a Creative Commons License and is copyrighted (C)2008 by Cory S.N. LaViska. |
---|
27 | // For details, visit http://creativecommons.org/licenses/by/3.0/us/ |
---|
28 | // |
---|
29 | */ |
---|
30 | if(jQuery) (function($){ |
---|
31 | |
---|
32 | $.extend($.fn, { |
---|
33 | multiSelect: function(o, callback) { |
---|
34 | // Default options |
---|
35 | if( !o ) var o = {}; |
---|
36 | if( o.selectAll == undefined ) o.selectAll = true; |
---|
37 | if( o.selectAllText == undefined ) o.selectAllText = "Select All"; |
---|
38 | if( o.noneSelected == undefined ) o.noneSelected = 'Select options'; |
---|
39 | if( o.oneOrMoreSelected == undefined ) o.oneOrMoreSelected = '% selected'; |
---|
40 | |
---|
41 | // Initialize each multiSelect |
---|
42 | $(this).each( function() { |
---|
43 | var select = $(this); |
---|
44 | var html = '<input type="text" readonly="readonly" class="multiSelect" value="" style="cursor: default;" />'; |
---|
45 | html += '<div class="multiSelectOptions" style="position: absolute; z-index: 99999; display: none;">'; |
---|
46 | if( o.selectAll ) html += '<label class="selectAll"><input type="checkbox" class="selectAll" />' + o.selectAllText + '</label>'; |
---|
47 | $(select).find('OPTION').each( function() { |
---|
48 | if( $(this).val() != '' ) |
---|
49 | html += '<label for="'+$(select).attr('name')+'">'+$(this).html() + '</label><input type="checkbox" name="' + $(select).attr('name') + '" value="' + $(this).val() + '"'+($(this).attr('selected')?' checked="checked"':'')+' />'; |
---|
50 | }); |
---|
51 | html += '</div>'; |
---|
52 | $(select).after(html); |
---|
53 | |
---|
54 | // Events |
---|
55 | $(select).next('.multiSelect').mouseover( function() { |
---|
56 | $(this).addClass('hover'); |
---|
57 | }).mouseout( function() { |
---|
58 | $(this).removeClass('hover'); |
---|
59 | }).click( function() { |
---|
60 | // Show/hide on click |
---|
61 | if( $(this).hasClass('active') ) { |
---|
62 | $(this).multiSelectOptionsHide(); |
---|
63 | } else { |
---|
64 | $(this).multiSelectOptionsShow(); |
---|
65 | } |
---|
66 | return false; |
---|
67 | }).focus( function() { |
---|
68 | // So it can be styled with CSS |
---|
69 | $(this).addClass('focus'); |
---|
70 | }).blur( function() { |
---|
71 | // So it can be styled with CSS |
---|
72 | $(this).removeClass('focus'); |
---|
73 | }); |
---|
74 | |
---|
75 | // Determine if Select All should be checked initially |
---|
76 | if( o.selectAll ) { |
---|
77 | var sa = true; |
---|
78 | $(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT:checkbox').not('.selectAll').each( function() { |
---|
79 | if( !$(this).attr('checked') ) sa = false; |
---|
80 | }); |
---|
81 | if( sa ) $(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT.selectAll').attr('checked', true).parent().addClass('checked'); |
---|
82 | } |
---|
83 | |
---|
84 | // Handle Select All |
---|
85 | $(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT.selectAll').click( function() { |
---|
86 | if( $(this).attr('checked') == true ) $(this).parent().parent().find('INPUT:checkbox').attr('checked', true).parent().addClass('checked'); else $(this).parent().parent().find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked'); |
---|
87 | }); |
---|
88 | |
---|
89 | // Handle checkboxes |
---|
90 | $(select).next('.multiSelect').next('.multiSelectOptions').find('INPUT:checkbox').click( function() { |
---|
91 | $(this).parent().parent().multiSelectUpdateSelected(o); |
---|
92 | $(this).parent().parent().find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked'); |
---|
93 | $(this).parent().parent().prev('.multiSelect').focus(); |
---|
94 | if( !$(this).attr('checked') ) $(this).parent().parent().find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked'); |
---|
95 | if( callback ) callback($(this)); |
---|
96 | }); |
---|
97 | |
---|
98 | // Initial display |
---|
99 | $(select).next('.multiSelect').next('.multiSelectOptions').each( function() { |
---|
100 | $(this).multiSelectUpdateSelected(o); |
---|
101 | $(this).find('INPUT:checked').parent().addClass('checked'); |
---|
102 | }); |
---|
103 | |
---|
104 | // Handle hovers |
---|
105 | $(select).next('.multiSelect').next('.multiSelectOptions').find('LABEL').mouseover( function() { |
---|
106 | $(this).parent().find('LABEL').removeClass('hover'); |
---|
107 | $(this).addClass('hover'); |
---|
108 | }).mouseout( function() { |
---|
109 | $(this).parent().find('LABEL').removeClass('hover'); |
---|
110 | }); |
---|
111 | |
---|
112 | // Keyboard |
---|
113 | $(select).next('.multiSelect').keydown( function(e) { |
---|
114 | // Is dropdown visible? |
---|
115 | if( $(this).next('.multiSelectOptions').is(':visible') ) { |
---|
116 | // Dropdown is visible |
---|
117 | // Tab |
---|
118 | if( e.keyCode == 9 ) { |
---|
119 | $(this).addClass('focus').trigger('click'); // esc, left, right - hide |
---|
120 | $(this).focus().next(':input').focus(); |
---|
121 | return true; |
---|
122 | } |
---|
123 | |
---|
124 | // ESC, Left, Right |
---|
125 | if( e.keyCode == 27 || e.keyCode == 37 || e.keyCode == 39 ) { |
---|
126 | // Hide dropdown |
---|
127 | $(this).addClass('focus').trigger('click'); |
---|
128 | } |
---|
129 | // Down |
---|
130 | if( e.keyCode == 40 ) { |
---|
131 | if( !$(this).next('.multiSelectOptions').find('LABEL').hasClass('hover') ) { |
---|
132 | // Default to first item |
---|
133 | $(this).next('.multiSelectOptions').find('LABEL:first').addClass('hover'); |
---|
134 | } else { |
---|
135 | // Move down, cycle to top if on bottom |
---|
136 | $(this).next('.multiSelectOptions').find('LABEL.hover').removeClass('hover').next('LABEL').addClass('hover'); |
---|
137 | if( !$(this).next('.multiSelectOptions').find('LABEL').hasClass('hover') ) { |
---|
138 | $(this).next('.multiSelectOptions').find('LABEL:first').addClass('hover'); |
---|
139 | } |
---|
140 | } |
---|
141 | return false; |
---|
142 | } |
---|
143 | // Up |
---|
144 | if( e.keyCode == 38 ) { |
---|
145 | if( !$(this).next('.multiSelectOptions').find('LABEL').hasClass('hover') ) { |
---|
146 | // Default to first item |
---|
147 | $(this).next('.multiSelectOptions').find('LABEL:first').addClass('hover'); |
---|
148 | } else { |
---|
149 | // Move up, cycle to bottom if on top |
---|
150 | $(this).next('.multiSelectOptions').find('LABEL.hover').removeClass('hover').prev('LABEL').addClass('hover'); |
---|
151 | if( !$(this).next('.multiSelectOptions').find('LABEL').hasClass('hover') ) { |
---|
152 | $(this).next('.multiSelectOptions').find('LABEL:last').addClass('hover'); |
---|
153 | } |
---|
154 | } |
---|
155 | return false; |
---|
156 | } |
---|
157 | // Enter, Space |
---|
158 | if( e.keyCode == 13 || e.keyCode == 32 ) { |
---|
159 | // Select All |
---|
160 | if( $(this).next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').hasClass('selectAll') ) { |
---|
161 | if( $(this).next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked') ) { |
---|
162 | // Uncheck all |
---|
163 | $(this).next('.multiSelectOptions').find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked'); |
---|
164 | } else { |
---|
165 | // Check all |
---|
166 | $(this).next('.multiSelectOptions').find('INPUT:checkbox').attr('checked', true).parent().addClass('checked'); |
---|
167 | } |
---|
168 | $(this).next('.multiSelectOptions').multiSelectUpdateSelected(o); |
---|
169 | if( callback ) callback($(this)); |
---|
170 | return false; |
---|
171 | } |
---|
172 | // Other checkboxes |
---|
173 | if( $(this).next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked') ) { |
---|
174 | // Uncheck |
---|
175 | $(this).next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked', false); |
---|
176 | $(this).next('.multiSelectOptions').multiSelectUpdateSelected(o); |
---|
177 | $(this).next('.multiSelectOptions').find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked'); |
---|
178 | // Select all status can't be checked at this point |
---|
179 | $(this).next('.multiSelectOptions').find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked'); |
---|
180 | if( callback ) callback($(this)); |
---|
181 | } else { |
---|
182 | // Check |
---|
183 | $(this).next('.multiSelectOptions').find('LABEL.hover INPUT:checkbox').attr('checked', true); |
---|
184 | $(this).next('.multiSelectOptions').multiSelectUpdateSelected(o); |
---|
185 | $(this).next('.multiSelectOptions').find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked'); |
---|
186 | if( callback ) callback($(this)); |
---|
187 | } |
---|
188 | } |
---|
189 | return false; |
---|
190 | } else { |
---|
191 | // Dropdown is not visible |
---|
192 | if( e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13 || e.keyCode == 32 ) { // down, enter, space - show |
---|
193 | // Show dropdown |
---|
194 | $(this).removeClass('focus').trigger('click'); |
---|
195 | $(this).next('.multiSelectOptions').find('LABEL:first').addClass('hover'); |
---|
196 | return false; |
---|
197 | } |
---|
198 | // Tab key |
---|
199 | if( e.keyCode == 9 ) { |
---|
200 | // Shift focus to next INPUT element on page |
---|
201 | $(this).focus().next(':input').focus(); |
---|
202 | return true; |
---|
203 | } |
---|
204 | } |
---|
205 | // Prevent enter key from submitting form |
---|
206 | if( e.keyCode == 13 ) return false; |
---|
207 | }); |
---|
208 | |
---|
209 | // Eliminate the original form element |
---|
210 | $(select).remove(); |
---|
211 | }); |
---|
212 | |
---|
213 | }, |
---|
214 | |
---|
215 | // Hide the dropdown |
---|
216 | multiSelectOptionsHide: function() { |
---|
217 | $(this).removeClass('active').next('.multiSelectOptions').hide(); |
---|
218 | }, |
---|
219 | |
---|
220 | // Show the dropdown |
---|
221 | multiSelectOptionsShow: function() { |
---|
222 | // Hide any open option boxes |
---|
223 | $('.multiSelect').multiSelectOptionsHide(); |
---|
224 | $(this).next('.multiSelectOptions').find('LABEL').removeClass('hover'); |
---|
225 | $(this).addClass('active').next('.multiSelectOptions').show(); |
---|
226 | |
---|
227 | // Position it |
---|
228 | var offset = $(this).offset(); |
---|
229 | $(this).next('.multiSelectOptions').css({ top: offset.top + $(this).outerHeight() + 'px' }); |
---|
230 | $(this).next('.multiSelectOptions').css({ left: offset.left + 'px' }); |
---|
231 | |
---|
232 | // Disappear on hover out |
---|
233 | multiSelectCurrent = $(this); |
---|
234 | var timer = ''; |
---|
235 | $(this).next('.multiSelectOptions').hover( function() { |
---|
236 | clearTimeout(timer); |
---|
237 | }, function() { |
---|
238 | timer = setTimeout('$(multiSelectCurrent).multiSelectOptionsHide(); $(multiSelectCurrent).unbind("hover");', 250); |
---|
239 | }); |
---|
240 | |
---|
241 | }, |
---|
242 | |
---|
243 | // Update the textbox with the total number of selected items |
---|
244 | multiSelectUpdateSelected: function(o) { |
---|
245 | var i = 0, s = ''; |
---|
246 | $(this).find('INPUT:checkbox:checked').not('.selectAll').each( function() { |
---|
247 | i++; |
---|
248 | }) |
---|
249 | if( i == 0 ) { |
---|
250 | $(this).prev('INPUT.multiSelect').val( o.noneSelected ); |
---|
251 | } else { |
---|
252 | if( o.oneOrMoreSelected == '*' ) { |
---|
253 | var display = ''; |
---|
254 | $(this).find('INPUT:checkbox:checked').each( function() { |
---|
255 | if( $(this).parent().text() != o.selectAllText ) display = display + $(this).parent().text() + ', '; |
---|
256 | }); |
---|
257 | display = display.substr(0, display.length - 2); |
---|
258 | $(this).prev('INPUT.multiSelect').val( display ); |
---|
259 | } else { |
---|
260 | $(this).prev('INPUT.multiSelect').val( o.oneOrMoreSelected.replace('%', i) ); |
---|
261 | } |
---|
262 | } |
---|
263 | } |
---|
264 | |
---|
265 | }); |
---|
266 | |
---|
267 | })(jQuery); |
---|