PageSourceSearch

https://hac4.dps.k12.oh.us/HomeAccess/Scripts/Trirand/ui.multiselect.js

js dps.k12.oh.us collected 2026-09-24 09:31:48 UTC 10,632 bytes, 315 lines download raw bytes

1/*
2 * jQuery UI Multiselect
3 *
4 * Authors:
5 *  Michael Aufreiter (quasipartikel.at)
6 *  Yanick Rochon (yanick.rochon[at]gmail[dot]com)
7 * 
8 * Dual licensed under the MIT (MIT-LICENSE.txt)
9 * and GPL (GPL-LICENSE.txt) licenses.
10 * 
11 * http://www.quasipartikel.at/multiselect/
12 *
13 * 
14 * Depends:
15 *	ui.core.js
16 *	ui.sortable.js
17 *
18 * Optional:
19 * localization (http://plugins.jquery.com/project/localisation)
20 * scrollTo (http://plugins.jquery.com/project/ScrollTo)
21 * 
22 * Todo:
23 *  Make batch actions faster
24 *  Implement dynamic insertion through remote calls
25 */
26
27
28(function($) {
29
30$.widget("ui.multiselect", {
31	_init: function() {
32		this.element.hide();
33		this.id = this.element.attr("id");
34		this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
35		this.count = 0; // number of currently selected options
36		this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container);
37		this.availableContainer = $('<div class="available"></div>').appendTo(this.container);
38		this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 '+$.ui.multiselect.locale.itemsCount+'</span><a href="#" class="remove-all">'+$.ui.multiselect.locale.removeAll+'</a></div>').appendTo(this.selectedContainer);
39		this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" class="add-all">'+$.ui.multiselect.locale.addAll+'</a></div>').appendTo(this.availableContainer);
40		this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer);
41		this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer);
42		
43		var that = this;
44
45		// set dimensions
46		this.container.width(this.element.width()+1);
47		this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation));
48		this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation)));
49
50		// fix list height to match <option> depending on their individual header's heights
51		this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1));
52		this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1));
53		
54		if ( !this.options.animated ) {
55			this.options.show = 'show';
56			this.options.hide = 'hide';
57		}
58		
59		// init lists
60		this._populateLists(this.element.find('option'));
61		
62		// make selection sortable
63		if (this.options.sortable) {
64			$("ul.selected").sortable({
65			    placeholder: 'ui-state-highlight',
66                connectWith: 'ul.available',
67				axis: 'y',
68				update: function(event, ui) {
69					// apply the new sort order to the original selectbox
70					that.selectedList.find('li').each(function() {
71						if ($(this).data('optionLink'))
72							$(this).data('optionLink').remove().appendTo(that.element);
73					});
74				},
75				receive: function(event, ui) {
76					ui.item.data('optionLink').attr('selected', true);
77					// increment count
78					that.count += 1;
79					that._updateCount();
80					// workaround, because there's no way to reference 
81					// the new element, see http://dev.jqueryui.com/ticket/4303
82					that.selectedList.children('.ui-draggable').each(function() {
83						$(this).removeClass('ui-draggable');
84						$(this).data('optionLink', ui.item.data('optionLink'));
85						$(this).data('idx', ui.item.data('idx'));
86						that._applyItemState($(this), true);
87					});
88			
89					// workaround according to http://dev.jqueryui.com/ticket/4088
90					setTimeout(function() { ui.item.remove(); }, 1);
91				}
92			});
93		}
94		
95		// set up livesearch
96		if (this.options.searchable) {
97			this._registerSearchEvents(this.availableContainer.find('input.search'));
98		} else {
99			$('.search').hide();
100		}
101		
102		// batch actions
103		$(".remove-all").click(function() {
104			that._populateLists(that.element.find('option').removeAttr('selected'));
105			return false;
106		});
107		$(".add-all").click(function() {
108			that._populateLists(that.element.find('option').attr('selected', 'selected'));
109			return false;
110		});
111	},
112	destroy: function() {
113		this.element.show();
114		this.container.remove();
115
116		$.widget.prototype.destroy.apply(this, arguments);
117	},
118	_populateLists: function(options) {
119		this.selectedList.children('.ui-element').remove();
120		this.availableList.children('.ui-element').remove();
121		this.count = 0;
122
123		var that = this;
124		var items = $(options.map(function(i) {
125	      var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show();
126
127			if (this.selected) that.count += 1;
128			that._applyItemState(item, this.selected);
129			item.data('idx', i);
130			return item[0];
131    }));
132		
133		// update count
134		this._updateCount();
135  },
136	_updateCount: function() {
137		this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount);
138	},
139	_getOptionNode: function(option) {
140		option = $(option);
141		var node = $('<li class="ui-state-default ui-element" title="'+option.text()+'"><span class="ui-icon"/>'+option.text()+'<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
142		node.data('optionLink', option);
143		return node;
144	},
145	// clones an item with associated data
146	// didn't find a smarter away around this
147	_cloneWithData: function(clonee) {
148		var clone = clonee.clone();
149		clone.data('optionLink', clonee.data('optionLink'));
150		clone.data('idx', clonee.data('idx'));
151		return clone;
152	},
153	_setSelected: function(item, selected) {
154		item.data('optionLink').attr('selected', selected);
155
156		if (selected) {
157			var selectedItem = this._cloneWithData(item);
158			item[this.options.hide](this.options.animated, function() { $(this).remove(); });
159			selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
160			
161			this._applyItemState(selectedItem, true);
162			return selectedItem;
163		} else {
164			
165			// look for successor based on initial option index
166			var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
167			var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));
168
169			// TODO: test needed for dynamic list populating
170			if ( direction ) {
171				while (i>=0 && i<items.length) {
172					direction > 0 ? i++ : i--;
173					if ( direction != comparator(item, $(items[i])) ) {
174						// going up, go back one item down, otherwise leave as is
175						succ = items[direction > 0 ? i : i+1];
176						break;
177					}
178				}
179			} else {
180				succ = items[i];
181			}
182			
183			var availableItem = this._cloneWithData(item);
184			succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
185			item[this.options.hide](this.options.animated, function() { $(this).remove(); });
186			availableItem.hide()[this.options.show](this.options.animated);
187			
188			this._applyItemState(availableItem, false);
189			return availableItem;
190		}
191	},
192	_applyItemState: function(item, selected) {
193		if (selected) {
194			if (this.options.sortable)
195				item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
196			else
197				item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
198			item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
199			this._registerRemoveEvents(item.find('a.action'));
200			
201		} else {
202			item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
203			item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
204			this._registerAddEvents(item.find('a.action'));
205		}
206		
207		this._registerHoverEvents(item);
208	},
209	// taken from John Resig's liveUpdate script
210	_filter: function(list) {
211		var input = $(this);
212		var rows = list.children('li'),
213			cache = rows.map(function(){
214				
215				return $(this).text().toLowerCase();
216			});
217		
218		var term = $.trim(input.val().toLowerCase()), scores = [];
219		
220		if (!term) {
221			rows.show();
222		} else {
223			rows.hide();
224
225			cache.each(function(i) {
226				if (this.indexOf(term)>-1) { scores.push(i); }
227			});
228
229			$.each(scores, function() {
230				$(rows[this]).show();
231			});
232		}
233	},
234	_registerHoverEvents: function(elements) {
235		elements.removeClass('ui-state-hover');
236		elements.mouseover(function() {
237			$(this).addClass('ui-state-hover');
238		});
239		elements.mouseout(function() {
240			$(this).removeClass('ui-state-hover');
241		});
242	},
243	_registerAddEvents: function(elements) {
244		var that = this;
245		elements.click(function() {
246			var item = that._setSelected($(this).parent(), true);
247			that.count += 1;
248			that._updateCount();
249			return false;
250		})
251		// make draggable
252		.each(function() {
253			$(this).parent().draggable({
254	      connectToSortable: 'ul.selected',
255				helper: function() {
256					var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
257					selectedItem.width($(this).width());
258					return selectedItem;
259				},
260				appendTo: '.ui-multiselect',
261				containment: '.ui-multiselect',
262				revert: 'invalid'
263	    });
264        });
265	},
266	_registerRemoveEvents: function(elements) {
267		var that = this;
268		elements.click(function() {
269			that._setSelected($(this).parent(), false);
270			that.count -= 1;
271			that._updateCount();
272			return false;
273		});
274 	},
275	_registerSearchEvents: function(input) {
276		var that = this;
277
278		input.focus(function() {
279			$(this).addClass('ui-state-active');
280		})
281		.blur(function() {
282			$(this).removeClass('ui-state-active');
283		})
284		.keypress(function(e) {
285			if (e.keyCode == 13)
286				return false;
287		})
288		.keyup(function() {
289			that._filter.apply(this, [that.availableList]);
290		});
291	}
292});
293		
294$.extend($.ui.multiselect, {
295	defaults: {
296		sortable: true,
297		searchable: true,
298		animated: 'fast',
299		show: 'slideDown',
300		hide: 'slideUp',
301		dividerLocation: 0.6,
302		nodeComparator: function(node1,node2) {
303			var text1 = node1.text(),
304			    text2 = node2.text();
305			return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
306		}
307	},
308	locale: {
309		addAll:'Add all',
310		removeAll:'Remove all',
311		itemsCount:'items selected'
312	}
313});
314
315})(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.