﻿
/*========================================================\
|##########################################################
|##########################################################
|----------------------------------------------------------
|   Class MultiSelect    
|---------------------------------------------------------:
|   2006 Jim Conte, Gabriels technology solutions
|   www.gabriels.net        www.jimconte.com
|---------------------------------------------------------:
|   Generates 2 boxes, where the user can select items
|   from the 'Source' box, and they are moved to the 
|   'Target' box. Can also move back to the 'Source'.
|   Object returns a comma delimited list of 'Target' keys
|----------------------------------------------------------
|   Styles defined in external sytlesheet
|   MultiSource : MultiTarget
|----------------------------------------------------------
|##########################################################
|##########################################################
`========================================================*/
 
function MultiSelect(SSID)
{
	this.MSSource   = null;
	this.MSTarget   = null;
	this.EachWidth  = 150;
	this.Height     = 100;
	this.Gap        = 10;
	this.ID         = SSID;
	this.AllButtons = false;
	this.defaultSRC = new Array();
	this.defaultPRE = new Array();
	this.arrSRC     = new Array();
	this.arrPRE     = new Array();
	this.arrTGT     = new Array();
	
	
	/*--------------------------------------------------------\
    |   defineID :  defines object ID for reference so it is 
    |               possible to have more than one per page  
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~: 
    |   defineSourceValues :  defines Source Array [2D array]
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   defineFormElement :  defines target Form Element
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   definePreSelectdValues : defines pre-selectd values
    |                            [1D array]
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   defineButtons : show add / remove all buttons
    `--------------------------------------------------------*/
    
	MultiSelect.prototype.defineID = function(newID)  { this.ID = newID; }
	MultiSelect.prototype.defineSourceValues = function(arrSourceValues) 
	{ 
	    this.arrSRC     = arrSourceValues; 
	    this.defaultSRC = arrSourceValues; 
	}
	MultiSelect.prototype.definePreSelectedValues = function(arrPreSelectedValues) 
	{ 
	    this.arrPRE     = arrPreSelectedValues; 
	    this.defaultPRE = arrPreSelectedValues;
	} 
	MultiSelect.prototype.defineButtons = function(bool) { this.AllButtons = bool; }
	
	/*--------------------------------------------------------\
    |   doReset     
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   resets form to default loaded values
    `--------------------------------------------------------*/
    MultiSelect.prototype.doReset = function() 
    {
        this.arrSRC = new Array();
        this.arrTGT = new Array();
        this.arrSRC     = this.defaultSRC;
        this.arrPRE     = this.defaultPRE;
        this.preload();
		this.sortData();
		this.loadSource(this.arrSRC);
		this.loadTarget(this.arrTGT);
    }
    
	/*--------------------------------------------------------\
    |   doLoadAgain     
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   loads form with new Pre Selected Data
    `--------------------------------------------------------*/
    MultiSelect.prototype.doLoadAgain = function(newArrayPre, newArraySrc) 
    {
        this.arrTGT = new Array();
        this.arrSRC = newArraySrc;
        this.arrPRE = newArrayPre;
		this.preload();
		this.sortData();
		this.loadSource(this.arrSRC);
		this.loadTarget(this.arrTGT);
    }
    
    /*--------------------------------------------------------\
    |   defineSize    
    |   requires [Int, Int, Int]  
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   defines total width, total height, and gap
    |   between box elements, each element width calculated
    `--------------------------------------------------------*/
	MultiSelect.prototype.defineSize = function(intWidth, intHeight, intGap)
	{
	    var eachWidth       = parseInt(((intWidth-intGap)/2), 10);
	    var trueGap         = parseInt(intWidth-(eachWidth*2), 10);
	        this.EachWidth  = eachWidth;
	        this.Height     = intHeight;
	        this.Gap        = trueGap;
	}

    /*--------------------------------------------------------\
    |   drawHTML    
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   writes boxes to page
    `--------------------------------------------------------*/
	MultiSelect.prototype.drawHTML = function()  
	{
		var strHTML = "";
		strHTML += "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>";
		strHTML += "<TR>";
		strHTML += "<TD><DIV ID='MultiSource" + this.ID + "' CLASS='MultiSource' STYLE='width:" + this.EachWidth + "px;height:" + this.Height + "px;'></DIV></TD>";
		strHTML += "<TD ALIGN='center'><DIV ID='MultiSpacer' STYLE='width:" + this.Gap + "px;text-align:center;'>";
		strHTML += "&nbsp;";
		strHTML += "</DIV>";
		
		if (this.AllButtons==true) {
		    
		    strHTML += "<a href=\"javascript:" + this.ID + ".AddAll();\" Class=\"searchb\" name=SelAll style=\"width:39px;height:18;margin-bottom:1px;\">&gt;&gt;&gt;</a>";
		    strHTML += "<BR>";
		    strHTML += "<a href=\"javascript:" + this.ID + ".RemoveAll();\" Class=\"searchb\" name=RemAll style=\"width:39px;height:18;margin-bottom:1px;\">&lt;&lt;&lt;</a>";
		    strHTML += "<BR>";
		    strHTML += "<a href=\"javascript:" + this.ID + ".doReset();\" Class=\"searchb\" name=ResetAll style=\"width:39px;height:18;margin-bottom:1px;\">Reset</a>";

//		    strHTML += "<img src=\"../Images/search/browseadd_norm.png\" width=39 height=18 border=0 name=SaveSearch onmouseover=\"this.src=browseadd_over.src;\" onmouseout=\"this.src=browseadd_norm.src;\" style=\"cursor:pointer;margin-top:3px;\" onclick=\"" + this.ID + ".AddAll();\"/>";
//		    strHTML += "<BR>";
//		    strHTML += "<img src=\"../Images/search/browseremove_norm.png\" width=39 height=18 border=0 name=SaveSearch onmouseover=\"this.src=browseremove_over.src;\" onmouseout=\"this.src=browseremove_norm.src;\" style=\"cursor:pointer;margin-top:3px;\" onclick=\"" + this.ID + ".RemoveAll();\"/>";
//		    strHTML += "<BR>";
//		    strHTML += "<img src=\"../Images/search/browsereset_norm.png\" width=39 height=18 border=0 name=SaveSearch onmouseover=\"this.src=browsereset_over.src;\" onmouseout=\"this.src=browsereset_norm.src;\" style=\"cursor:pointer;margin-top:3px;\" onclick=\"" + this.ID + ".doReset();\" VSPACE=10/>";
		    
		} else {
		    strHTML += "&nbsp;";
		}
		
		
		strHTML += "<TD><DIV ID='MultiTarget" + this.ID + "' CLASS='MultiTarget' STYLE='width:" + this.EachWidth + "px;height:" + this.Height + "px;'></DIV></TD>";
		strHTML += "</TR>";
		strHTML += "</TABLE>\n";
		
		document.write(strHTML);
	}
	
    /*--------------------------------------------------------\
    |   loadSource    
    |   requires [Array Object]  
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   loads contents of 2D array into this.MSSource
    `--------------------------------------------------------*/
	MultiSelect.prototype.loadSource = function(newArray)  
	{
		this.arrSRC = newArray;
		this.MSSource.innerHTML = this._Load(newArray,'add');
		this.returnTargetKeys();
	}
	
	/*--------------------------------------------------------\
    |   loadTarget    
    |   requires [Array Object]  
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   loads contents of 2D array to this.MSTarget
    `--------------------------------------------------------*/
	MultiSelect.prototype.loadTarget = function(newArray)  
	{
		this.arrTGT = newArray;
		this.MSTarget.innerHTML = this._Load(newArray,'remove');
		this.returnTargetKeys();
	}
	
	/*--------------------------------------------------------\
    |   _Load    
    |   requires [Array Object] [String:functionName]
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   returns a atring consisting of links for box
    |   generic constructor used for loadTarget and loadSource
    `--------------------------------------------------------*/
	MultiSelect.prototype._Load = function(newArray, functionName)  
	{
	    if (functionName=='add')
	    {
	        var widthOffset = 35;
	    }
	    else
	    {
	        var widthOffset = 45;
	    }
	    
		if (newArray.length>0)
		{
			var tmpString = "";
			for (x=0;x<newArray.length;x++) 
			{
				tmpString += "<A HREF=\"javascript:" + this.ID + "." + functionName + "('" + newArray[x][0] + "');\" STYLE=\"width:" + (this.EachWidth - widthOffset) + "px;\">" + newArray[x][1] + "</A>";
			}
			return tmpString;
		}
		else
		{
			return "";
		}
	}
	
	/*--------------------------------------------------------\
    |   AddAll    
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   moves all elements, if any, from Source to Target
    `--------------------------------------------------------*/
	MultiSelect.prototype.AddAll = function()  
	{
		if (this.arrSRC.length > 0) 
		{
		    var arrTempTarget  = this.arrTGT.concat(this.arrSRC);
		        this.arrTGT = arrTempTarget;
		        this.arrSRC = new Array();
		        this.sortData();
		        this.loadTarget(this.arrTGT);
		        this.loadSource(this.arrSRC);
		}
	}
	
	/*--------------------------------------------------------\
    |   RemoveAll    
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   moves all elements, if any, from Source to Target
    `--------------------------------------------------------*/
	MultiSelect.prototype.RemoveAll = function()  
	{
		if (this.arrTGT.length > 0) 
		{
	        var arrTempSource  = this.arrSRC.concat(this.arrTGT);
		        this.arrSRC = arrTempSource;
		        this.arrTGT = new Array();
		        this.sortData();
		        this.loadTarget(this.arrTGT);
		        this.loadSource(this.arrSRC);
		}
	}
	
	/*--------------------------------------------------------\
    |   expose{*} - exposes various arrays
    `--------------------------------------------------------*/
	MultiSelect.prototype.exposeArrSRC = function(){ return this.arrSRC; }
	MultiSelect.prototype.exposeArrTGT = function(){ return this.arrTGT; }
	MultiSelect.prototype.exposeDefaultArrSRC = function(){ return this.defaultSRC; }
	MultiSelect.prototype.exposeDefaultArrPRE = function(){ return this.defaultPRE; }
	
	/*--------------------------------------------------------\
    |   preload
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   1. loads pre-selected values to arrTGT
    |   2. removes pre-selected values from arrSRC
    |   3. uses inArray function defined in Global.js
    |   4. arrSRC and arrTGT must be defined first
    `--------------------------------------------------------*/
	MultiSelect.prototype.preload = function()  
	{
	    var arrTempSource  = new Array();
		var arrTempTarget  = new Array();
	    if (this.arrPRE.length>0 && this.arrSRC.length>0)
	    {
            for (p=0;p<this.arrSRC.length;p++)
            {
                if (this.inArray(this.arrPRE, this.arrSRC[p][0]))
                {
                    arrTempTarget.push(this.arrSRC[p]);
                } 
                else 
                {
                    arrTempSource.push(this.arrSRC[p]);
                }
            }
            this.arrSRC = arrTempSource;
            this.arrTGT = arrTempTarget;
        }
	}
	
	/*--------------------------------------------------------\
    |   add    
    |   requires [String ItemID]
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   moves element from this.MSSource to this.MSTarget
    `--------------------------------------------------------*/
	MultiSelect.prototype.add = function(itemID)  {
		var arrTempSource  = new Array();
		var arrTempTarget  = new Array();
			arrTempTarget  = this.arrTGT;
		
		if (this.arrSRC.length > 1) 
		{
			for (x=0;x<this.arrSRC.length;x++) 
			{
				if (this.arrSRC[x][0]==itemID) 
				{
					arrTempTarget.push(this.arrSRC[x]);
				} 
				else 
				{
					arrTempSource.push(this.arrSRC[x]);
				}
			}
		} 
		else 
		{
			arrTempTarget.push(this.arrSRC[0]);
			arrTempSource = new Array();
		}
		this.sortData();
		this.loadTarget(arrTempTarget);
		this.loadSource(arrTempSource);
	}
	
	/*--------------------------------------------------------\
    |   remove
    |   requires [String ItemID]
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   moves element from this.MSTarget to this.MSSource 
    `--------------------------------------------------------*/
	MultiSelect.prototype.remove = function(itemID)  
	{
		var arrTempSource  = new Array();
		var arrTempTarget  = new Array();
			arrTempSource  = this.arrSRC;
		if (this.arrTGT.length > 1) 
		{
			for (x=0;x<this.arrTGT.length;x++) 
			{
				if (this.arrTGT[x][0]==itemID) 
				{
					arrTempSource.push(this.arrTGT[x]);
				} 
				else 
				{
					arrTempTarget.push(this.arrTGT[x]);
				}
			}
		} 
		else 
		{
			arrTempSource.push(this.arrTGT[0]);
			arrTempTarget = new Array();
		}
		this.sortData();
		this.loadSource(arrTempSource);
		this.loadTarget(arrTempTarget);
	}
	
	/*--------------------------------------------------------\
    |   mySortFunction
    |   requires [2D Array] passed by reference
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   Sorts dimention [X] on 2D array
	|   X = defines which dimention elements are sorted by
	|   0 = keys
	|   1 = values
    `--------------------------------------------------------*/
	MultiSelect.prototype.mySortFunction = function(a,b)  
	{
		if (a[1]<b[1]) return -1;
		if (a[1]>b[1]) return 1;
		return 0;
	}
	
	/*--------------------------------------------------------\
    |   sortData
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   Sorts arrSRC and arrTGT defined by this.mySortFunction
    `--------------------------------------------------------*/
	MultiSelect.prototype.sortData = function()  
	{
	    //-- this is the big time consumer.
	    if (this.arrSRC.length < 200) {
	        this.arrSRC = this.arrSRC.sort(this.mySortFunction);
	    }
	    if (this.arrTGT.length < 200) {
	        this.arrTGT = this.arrTGT.sort(this.mySortFunction);
	    }
	}
	
	/*--------------------------------------------------------\
    |   returnTargetKeys
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |   sends comma delimited list of arrTGT keys 
    |   to this.FormObject
    `--------------------------------------------------------*/
	MultiSelect.prototype.returnTargetKeys = function()  
	{
		if (this.arrTGT.length > 0)
		{
		    var strTemp = "";
			for (x=0;x<this.arrTGT.length;x++) 
			{
				strTemp += this.arrTGT[x][0];
				if (x<this.arrTGT.length-1)  
				{
					strTemp += ",";
				}
			}
		}
		else
		{
			strTemp = "";
		}
		return strTemp;
 	}
 
    /*--------------------------------------------------------\
    |   initialize
    |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:
    |  1. Writes HTML to DOM
    |  2. Defines [DIV Elements] in DOM
    |  3. Readies [Arrays] for loading
    |  3. Loads [Arrays] into [DIV Elements]
    `--------------------------------------------------------*/
	MultiSelect.prototype.initialize = function()  
	{
        with (this) 
        {
	        drawHTML();
	        MSSource = document.getElementById('MultiSource' + ID);
	        MSTarget = document.getElementById('MultiTarget' + ID);
	        preload();
	        sortData();
	        loadSource(arrSRC);
	        loadTarget(arrTGT);
	        //returnTargetKeys();
	    }
	}
	
	
	/*========================================================\
    |   inArray  
    |   requires [Array] [String:value]
    |---------------------------------------------------------:
    |   returns True if found, False if not
    |   uses "===" for exact match, not just similar
    `========================================================*/
    MultiSelect.prototype.inArray = function(targetArray, value)
    {
        var i;
        for (i=0;i<targetArray.length;i++) 
        {
            if (this.trim(targetArray[i]+" ") === this.trim(value+" ")) // make sure both are strings for comparison
            {
                return true;
            }
        }
        return false;
    }
    
    
    /*========================================================,
    |   trim | requires [String]
    |---------------------------------------------------------:
    |   returns [String] without leading and trailing spaces
    `========================================================*/

    MultiSelect.prototype.trim = function(str) 
    {
	    return str.replace(/^\s*|\s*$/g,'');
    }

}
/*========================================================\
|##########################################################
|##########################################################
|----------------------------------------------------------
|   END Class MultiSelect    
|---------------------------------------------------------:
|##########################################################
|##########################################################
`========================================================*/
