/****************************************************************************************
     Script to make select-multiple inputs respond better to mouse-only interaction
                   Written by Mark Wilton-Jones, 27/01/2008-06/02/2008
                                       Version 1.0
*****************************************************************************************

Please see http://www.howtocreate.co.uk/jslibs/ for details and a demo of this script
Please see http://www.howtocreate.co.uk/jslibs/termsOfUse.html for terms of use

Recommended: use checkboxes instead of this script.

To use:

Inbetween the <head> tags, put:

	<script src="PATH TO SCRIPT/mousemultiselect.js" type="text/javascript"></script>

The script offers three functions for adding more convenient mouse interaction to a
select-multiple input:

mouseMultiSelect(object: selectInput)
	selectInput = a reference to a select-multiple input.
	Prepares the given select-multiple input.
mouseMultiSelects()
	Searches for all select-multiple inputs that are currently on the page, and prepares them.
onloadMouseMultiSelects()
	Tells the script to search for all select-multiple inputs as soon as the page has loaded,
	or as soon as the HTML has been parsed (depending on the browser).
	Can be called at any time before the page completes loading.

If the options for an input are going to be created by a script, and some of them may have
their .selected property set by script, then it is important not to tell this script to set
them up until all of their options have been correctly set up. Each select-multiple input
will only be set up once. Repeated attempts to set up an input will be ignored.
___________________________________________________________________________________________*/

function mouseMultiSelect(oSel) {
	function updateSelect(oSel) {
		//store statuses and set colours to reduce flicker
		for( i = 0; i < oSel.options.length; i++ ) {
			oSel.selectedList[i] = oSel.options[i].selected;
			//does not style in Opera - dynamic changes to option styles do not work after initial paint - pity
			if( !window.opera ) {
				//could use Highlight/HighlightText but it's nicer to see which ones will change
				oSel.options[i].style.background = oSel.selectedList[i] ? 'teal' : '';
				oSel.options[i].style.color = oSel.selectedList[i] ? 'white' : '';
			}
		}
		oSel.className += ''; //relieve IE 8 redraw bug
	}
	function selectChange(e) {
		var oSel = e.currentTarget ? e.currentTarget : event.srcElement;
		while( !oSel.options ) { oSel = oSel.parentNode; } //just in case IE starts making the OPTION or OPTGROUP the srcElement
		if( e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || ( e.target && e.target != oSel && !e.target.selected ) ) {
			//don't change it if the user is using modifier keys already
			//browsers (like NetFront) can already deselect when clicking selected options - detect if they unselected the target option
			//also makes clicks and drags on optgroup slightly more resilient
			//also prevents accidental mouseup from clearing the selection except when over the only selected option
			updateSelect(oSel);
			return;
		}
		//for drag-select and multi-click selection (like NetFront), make it OR instead of XOR - more intuitive and avoids breaking multi-click
		var multi = 0, i;
		for( i = 0; i < oSel.options.length && multi < 2; i++ ) {
			if( oSel.options[i].selected ) { multi++; }
		}
		multi = multi > 1;
		//set .selected as needed
		for( i = 0; i < oSel.options.length; i++ ) {
			if( multi ) {
				oSel.options[i].selected = oSel.selectedList[i] || oSel.options[i].selected;
			} else {
				oSel.options[i].selected = !oSel.selectedList[i] != !oSel.options[i].selected;
			}
		}
		updateSelect(oSel);
	}
	function updateWithoutChange(e) {
		e = e.currentTarget ? e.currentTarget : event.srcElement;
		while( !e.options ) { e = e.parentNode; } //just in case IE starts making the OPTION or OPTGROUP the srcElement
		updateSelect(e);
	}
	if( oSel.multiple && !oSel.selectedList ) {
		oSel.selectedList = [];
		updateSelect(oSel);
		if( oSel.addEventListener ) {
			//click does not fire on drag-select for many browsers
			oSel.addEventListener('mouseup',selectChange,false);
			oSel.addEventListener('keydown',updateWithoutChange,false);
			oSel.addEventListener('keypress',updateWithoutChange,false);
			oSel.addEventListener('keyup',updateWithoutChange,false);
		} else if( oSel.attachEvent ) {
			//IE has trouble with mouseup but click also catches drag-select
			oSel.attachEvent('onclick',selectChange);
			oSel.attachEvent('onkeydown',updateWithoutChange);
			oSel.attachEvent('onkeypress',updateWithoutChange);
			oSel.attachEvent('onkeyup',updateWithoutChange);
		}
	}
}
function mouseMultiSelects() {
	for( var i = 0, s = document.getElementsByTagName('select'); i < s.length; i++ ) {
		mouseMultiSelect(s[i]);
	}
}
function onloadMouseMultiSelects() {
	if( window.addEventListener ) {
		window.addEventListener('DOMContentLoaded',mouseMultiSelects,true);
		window.addEventListener('load',mouseMultiSelects,true);
	} else if( document.addEventListener ) {
		document.addEventListener('DOMContentLoaded',mouseMultiSelects,true);
		document.addEventListener('load',mouseMultiSelects,true);
	} else if( window.attachEvent ) {
		window.attachEvent('onload',mouseMultiSelects);
	}
}