/** dual select widget - bfults@gmail.com - 2005-07                          **
 ** Code licensed under the Academic Free License v2.1                       **
 ** http://www.opensource.org/licenses/afl-2.1.txt                           **/

/*
	Usage:
		- Include this script in the <head> of your HTML document.
		<script type="text/javascript" src="dual-select-widget.js"></script>
		
		- Give each select that you want to turn into dual selects a class
		  of "dualsels".
		  <select multiple="multiple" class="dualsels" ...> ...
*/

var SIZE_SELECT = 10;
var DS_UNIQUE = 0;
var DS_REFS = [];

AttachEvent(window, "load", dualSelectAttach);

function dualSelectAttach()
{
	var nlForms = document.getElementsByTagName("FORM"), nlSels = null;
	var hasDS = false;
	
	// for each form on the page
	for (var i=0,j=0,len=nlForms.length; i < len; i++)
	{
		hasDS = false;
		
		// for each select in the form
		nlSels = nlForms[i].getElementsByTagName("SELECT");
		for (j=0,men=nlSels.length; j < men; j++)
		{
			// if "dualsels" is one of the classes assigned to the select
			if (/\bdualsels\b/.test(nlSels[j].className))
			{
				// make sure the select has an id
				if (nlSels[j].id == '')
					nlSels[j].id = "DS_SEL"+i;

				nlSels[j].style.display = "none";
				createDualSelect(nlSels[j].id);
				hasDS = true;
			}
		}
		
		if (hasDS == true)
			AttachEvent(nlForms[i], "submit", function() {
				return destroyDualSelects(this);
			});
	}
	return true;
}

function createDualSelect(sID)
{
	var elOrig = document.getElementById(sID);
	
	// new HTML for UI (innerHTML faster than DOM + better IE support)
	var sNewHTML = '<table style="border-collapse:collapse" class="dualselc">'+
		'<tr><td style="vertical-align:middle"><select multiple="multiple" '+
		'size="'+SIZE_SELECT+'" id="DS_sel1'+DS_UNIQUE+'"></select></td>'+
		'<td style="vertical-align:middle"><button type="button" '+
		'onclick="dualSelectLTR('+DS_UNIQUE+')">&raquo;</button><br />'+
		'<button type="button" onclick="dualSelectRTL('+DS_UNIQUE+')">&laquo;'+
		'</button></td><td style="vertical-align:middle">'+
		'<select multiple="multiple" size="'+SIZE_SELECT+'" id="DS_sel2'+
		DS_UNIQUE+'"></select></td></tr></table>';
	
	// create a new container and put it right before the original select
	var elNewCont = document.createElement("div");
	elOrig.parentNode.insertBefore(elNewCont, elOrig);
	
	// stick UI into new container
	elNewCont.innerHTML = sNewHTML;
	
	// grab refs to new selects
	var elNewSel1 = document.getElementById("DS_sel1"+DS_UNIQUE);
	var elNewSel2 = document.getElementById("DS_sel2"+DS_UNIQUE);
	
	// populate selects
	for (var i=0,len=elOrig.options.length; i < len; i++)
	{
 		if (elOrig.options[i].selected == true)
			addOption(elOrig.options[i], elNewSel2);
		else
			addOption(elOrig.options[i], elNewSel1);
	}
	
	// deselect all options in new selects
	elNewSel1.selectedIndex = elNewSel2.selectedIndex = -1;
	
	// save the elements involved in a hash
	DS_REFS[DS_UNIQUE] = {cont: elNewCont, sel1: elNewSel1, sel2: elNewSel2,
		orig: elOrig};
	elOrig.DS_UNIQUE = DS_UNIQUE;
	
	// increment the unique id
	DS_UNIQUE++;
	
	return true;
}

function dualSelectLTR(nID)
{
	// grab refs to the elements
	var els = DS_REFS[nID];
	if (!els) return;
	
	var s1 = els["sel1"], s2 = els["sel2"];
	return dualSelectMoveSelected(s1, s2);
}

function dualSelectRTL(nID)
{
	// grab refs to the elements
	var els = DS_REFS[nID];
	if (!els) return;
	
	var s1 = els["sel1"], s2 = els["sel2"];
	return dualSelectMoveSelected(s2, s1);
}

function dualSelectMoveSelected(s1, s2)
{
	var i = 0, len = s1.options.length, lenNew = len;
	var ixAvail = [];
	
	while (i < len)
	{
		if (s1.options[i] && s1.options[i].selected == true)
		{
			// deselect it and move it to s2
			s1.options[i].selected = false;
			addOption(s1.options[i], s2);
			
			// remove the option from s1
			s1.removeChild(s1.options[i]);
			len--;
			
			// continue because the next option got sucked into this spot
			continue;
		}
		i++;
	}

	return true;
}

function destroyDualSelects(elForm)
{
	// destroy each saved instance
	for (var i=0,len=DS_REFS.length; i < len; i++)
		destroyDualSelect(DS_REFS[i]["orig"].id);
	
	// clean up
	try { delete window.DS_REFS; } catch(e) {}
	
	return true;
}

function destroyDualSelect(sID)
{
	// obtain a ref to the select and grab its DS components
	var elOrig = document.getElementById(sID);
	if (!elOrig) return;
	var els = DS_REFS[elOrig.DS_UNIQUE];
	if (!els) return;
	
	// grab refs to new selects and container
	elNewCont = els["cont"];
	elNewSel1 = els["sel1"];
	elNewSel2 = els["sel2"];
	
	// clear out the stagnant options
	elOrig.options.length = 0;
	
	// add the new options from the unselected select
	for (var i=0,len=elNewSel1.options.length; i < len; i++)
	{
		elNewSel1.options[i].selected = false;
		addOption(elNewSel1.options[i], elOrig);
	}
	
	// add the new options from the selected select
	for (i=0,len=elNewSel2.options.length; i < len; i++)
	{
		elNewSel2.options[i].selected = true;
		addOption(elNewSel2.options[i], elOrig);
	}
	
	// destroy our UI
	elNewCont.parentNode.removeChild(elNewCont);
	
	// restore display of original select
	elOrig.style.display = '';
	
	return true;
}
	
function addOption(elOp, elSel)
{
	var elNewOp = elSel.appendChild(document.createElement("option"));
	elNewOp.text = elOp.text;
	elNewOp.value = elOp.value;
	elNewOp.selected = elOp.selected;
	return true;
}

// cross-browser event handler functions
function AttachEvent(el, evt, fn, cap)
{
	if (!cap) cap = false;
	if (el.attachEvent)
		return el.attachEvent("on"+evt, fn);
	else if (el.addEventListener)
		return (el.addEventListener(evt, fn, cap), true);
	return false;
}
function DetachEvent(el, evt, fn, cap)
{
	if (!cap) cap = false;
	if (el.detachEvent)
		return el.detachEvent("on"+evt, fn);
	else if (el.removeEventListener)
		return (el.removeEventListener(evt, fn, cap), true);
	return false;
}


