/**
 * Class for a multiple selector.
 * @param       string       name of the object instantiation
 * @param       string       id of the div for the pickers
 * @param       string       API path to get the select list
 * @param       string       API path to get the currently selected list
 */
function MultiSelect (sObjName, sDivId, sListTitle, sListUrl, sSelectedTitle, sSelectedUrl)
{
    this.sObjName        = sObjName;                                // name of instantiation
    this.sDivId          = sDivId;                                  // main div holding multi selector
    this.sListUrl        = sListUrl + '?id=' + this.sObjName;       // path for full list xml
    this.sSelectedUrl    = sSelectedUrl + '?id=' + this.sObjName;   // path for selected list xml

    this.sListTitle      = sListTitle;                              // title for the full list
    this.sSelectedTitle  = sSelectedTitle;                          // title for the selected list
    this.sSearchTitle    = 'Search Results';                        // search results title
    
    this.oList           = new Object();                            // full list select object
    this.oSearch         = new Object();                            // search select object
    this.oActive         = new Object();                            // holds active select list or search                              
    this.oSelected       = new Object();                            // selected select object  
    this.nSize           = 10;                                      // size of select boxes
    this.nWidth          = '200px';                                 // width of select boxes
    this.sInput          = 'search';                                // initial search input text    
    
    this.init();
}

/**
 * run on instantiation draws the selects to the screen and sets up the lists
 */
MultiSelect.prototype.init = function ()
{
    var aInnerHtml = new Array();
    aInnerHtml.push('<input type="hidden" id="' + this.sObjName + '_list" name="' + this.sObjName + '_list[]" value="" />');
    aInnerHtml.push('<table>');
        aInnerHtml.push('<tr>');
            aInnerHtml.push('<td colspan="3">');
                aInnerHtml.push('<input type="text" id="' + this.sObjName + '_search" value="' + this.sInput + '" autocomplete="off" onkeypress="if (' + this.sObjName + '.checkKeyPress(event)) return false;" onkeyup="' + this.sObjName + '.search(event, this.value);" onfocus="this.value = \'\';" />');
                aInnerHtml.push('<span id="' + this.sObjName + '_title"></span>');
            aInnerHtml.push('</td>');
        aInnerHtml.push('</tr>');
        aInnerHtml.push('<tr>');
            aInnerHtml.push('<td>');
                aInnerHtml.push('<div id="' + this.sObjName + '_list_div">');
                    aInnerHtml.push(this.sListTitle + '<br />');
                    aInnerHtml.push('<select id="' + this.sObjName + '_list_select" size="' + this.nSize + '" style="width: ' + this.nWidth + '" ondblclick="' + this.sObjName + '.addSelected();" multiple></select>');                
                aInnerHtml.push('</div>');
                aInnerHtml.push('<div id="' + this.sObjName + '_search_div" style="display: none">');
                    aInnerHtml.push(this.sSearchTitle + '<br />');
                    aInnerHtml.push('<select id="' + this.sObjName + '_search_select" size="' + this.nSize + '" style="width: ' + this.nWidth + '" ondblclick="' + this.sObjName + '.addSelected();" multiple></select>');                
                aInnerHtml.push('</div>');
            aInnerHtml.push('</td>');
            aInnerHtml.push('<td style="text-align: center">');
                aInnerHtml.push('<button type="button" onclick="' + this.sObjName + '.addSelected()">&gt;&gt;</button>');
                aInnerHtml.push('<br />');
                aInnerHtml.push('<button type="button" onclick="' + this.sObjName + '.removeSelected()">&lt;&lt;</button>');
                aInnerHtml.push('<br /><br />');
                aInnerHtml.push('<button type="button" onclick="' + this.sObjName + '.addAll()">add all</button>');
                aInnerHtml.push('<br /><br />');                
                aInnerHtml.push('<button type="button" onclick="' + this.sObjName + '.removeAll()">clear</button>');                
            aInnerHtml.push('</td>');
            aInnerHtml.push('<td>');
                aInnerHtml.push('<div id="' + this.sObjName + '_selected_div">');
                    aInnerHtml.push(this.sSelectedTitle + '<br />');
                    aInnerHtml.push('<select id="' + this.sObjName + '_selected_select" size="' + this.nSize + '" style="width: ' + this.nWidth + '" ondblclick="' + this.sObjName + '.removeSelected();" multiple></select>');
                aInnerHtml.push('</div>');
            aInnerHtml.push('</td>');
    aInnerHtml.push('</table>');   
    
    document.getElementById(this.sDivId).innerHTML = aInnerHtml.join("\n"); 

    this.oList     = document.getElementById(this.sObjName + '_list_select');
    this.oSearch   = document.getElementById(this.sObjName + '_search_select');
    this.oSelected = document.getElementById(this.sObjName + '_selected_select');
    this.setActive('list');  // start with full list as active 
    this.loadList();
    if (this.sSelectedUrl) this.loadSelected();
}

/**
 * Set active list, either search or list, display the new active one
 * hide the other one.
 * @param         string          list or search
 */
MultiSelect.prototype.setActive = function (sActive)
{
    if (sActive == 'list')
    {
        this.oActive = this.oList;
        document.getElementById(this.sObjName + '_list_div').style.display   = 'block';
        document.getElementById(this.sObjName + '_search_div').style.display = 'none';
        document.getElementById(this.sObjName + '_title').innerHTML = '';
    }
    else if (sActive == 'search')
    {
        this.oActive = this.oSearch;
        document.getElementById(this.sObjName + '_search_div').style.display = 'block';    
        document.getElementById(this.sObjName + '_list_div').style.display   = 'none';
        document.getElementById(this.sObjName + '_title').innerHTML = '<a href="#" onclick="' + this.sObjName + '.setActive(\'list\');">back to ' + this.sListTitle + '</a>';
    }
}

/**
 * Grab the list and load it with Ajax
 */
MultiSelect.prototype.loadList = function ()
{
    var req = new ajaxQuery(this.sListUrl, this.processLoadList);
}

/**
 * Process the list
 * @param      int       ajax req number
 * @param      object    xml request object
 */
MultiSelect.prototype.processLoadList = function (n, response)
{
    if (!checkXML(response)) return;

    var sObjName = response.getElementsByTagName('object_name')[0].firstChild.data;
    var obj = document.getElementById(sObjName + '_list_select');  
    var listXML = response.getElementsByTagName('item');
    
    for (var i = 0; i < listXML.length; i++)
    {
        obj.options[obj.options.length] = new Option(listXML[i].firstChild.data, listXML[i].getAttribute('id'));
    }
}

/**
 * Grab the selected list
 */
MultiSelect.prototype.loadSelected = function ()
{
    var req = new ajaxQuery(this.sSelectedUrl, this.processLoadSelected);
}

/**
 * Process the list
 * @param      int       ajax req number
 * @param      object    xml request object
 */
MultiSelect.prototype.processLoadSelected = function (n, response)
{
    if (!checkXML(response)) return;

    var sObjName = response.getElementsByTagName('object_name')[0].firstChild.data;
    var obj = document.getElementById(sObjName + '_selected_select'); 
    var listXML = response.getElementsByTagName('item');

    var aTmp = new Array();
    for (var i = 0; i < listXML.length; i++)
    {
        obj.options[obj.options.length] = new Option(listXML[i].firstChild.data, listXML[i].getAttribute('id'));
        aTmp.push(listXML[i].getAttribute('id'));
    }
    document.getElementById(sObjName + '_list').value = aTmp;
}
    
/**    
 * Add the selected item to the selected select box
 */ 
MultiSelect.prototype.addSelected = function ()
{
    if (this.oActive.selectedIndex != -1) {	
        for (var i = 0; i < this.oActive.options.length; i++)
        {
            if (this.oActive.options[i].selected)
            {
                if (!this.isSelected(this.oActive.options[i].value))
                {
                    this.oSelected.options[this.oSelected.options.length] = new Option(this.oActive.options[i].text, this.oActive.options[i].value);
                    this.oSelected.selectedIndex = (this.oSelected.options.length - 1);
                }
            }	
        }
    }	
    this.buildList();
}

/**
 * Add the entire list
 */
MultiSelect.prototype.addAll = function ()
{
    for (var i = 0; i < this.oActive.options.length; i++)
    {
        if (!this.isSelected(this.oActive.options[i].value))
        {
            this.oSelected.options[this.oSelected.options.length] = new Option(this.oActive.options[i].text, this.oActive.options[i].value);
            this.oSelected.selectedIndex = (this.oSelected.options.length - 1);
        }
    }
    this.buildList();	
}

/**
 * Remove the selected item from the selected select box
 */
MultiSelect.prototype.removeSelected = function ()
{
    if (this.oSelected.selectedIndex != -1)
    {	
        for (var i = (this.oSelected.options.length - 1); i >= 0; i--)
        {
            if (this.oSelected.options[i].selected)
            {
                this.oSelected.options[i] = null;
            }	
        }
    }
    this.buildList();	
}

/**
 * clear the selected select box
 */
MultiSelect.prototype.removeAll = function ()
{
    for (var i = (this.oSelected.options.length - 1); i >= 0; i--)
    {
        this.oSelected.options[i] = null;
    }
    this.buildList();	
}

/**
 * Is the current item selected ?
 * @param       int      id for the item
 * @return      boolean
 */
MultiSelect.prototype.isSelected = function (nId)
{
    var bReturn = false;
    for (var i = 0; !bReturn && i < this.oSelected.options.length; i++)
    {
        bReturn = (nId == this.oSelected.options[i].value) ? true : false;
    }
    return bReturn;
}

MultiSelect.prototype.checkKeyPress = function (event)
{
    switch (event.keyCode)
    {
        case 13:  // enter
            this.addSelected();
            return true;
        case 38:  // up arrow
            this.selectPrevious();
            return true;
        case 40:  // down arrow
            this.selectNext();
            return true;
        default:
            return false;
    }
}

MultiSelect.prototype.selectPrevious = function ()
{
    if (this.oActive.selectedIndex != -1 && this.oActive.selectedIndex != 0)
    {
        this.oActive.selectedIndex = this.oActive.selectedIndex - 1;
    }
}
// fix this onkeyup is resetting selected index and protect against next index do same for prev
MultiSelect.prototype.selectNext = function ()
{
    if (this.oActive.selectedIndex != -1 && this.oActive.selectedIndex != (this.oActive.length - 1))
    {
        this.oActive.selectedIndex = this.oActive.selectedIndex + 1;
    }
}

/**
 * populate the hidden variable with an array of selected id's, this is because
 * otherwise you have to have the items in the selected box actually selected
 * to get them out of the POST or GET vars
 */
MultiSelect.prototype.buildList = function ()
{
    var aTmp = new Array();
    for (var i = 0; i < this.oSelected.options.length; i++)
    {
        aTmp.push(this.oSelected.options[i].value);            
    }	    
    document.getElementById(this.sObjName + '_list').value = aTmp;
}

/**
 * Perform the search
 * @param       string       search query
 */
MultiSelect.prototype.search = function (event, sSearch)
{   
    if (event.keyCode == 13 || event.keyCode == 38 || event.keyCode == 40) return;
    
    this.oSearch.length = 0;                           // clear previous search results
    sSearch     = sSearch.replace(/^\s+|\s+$/g, '');   // trim
    sSearch     = sSearch.replace(/\s+/, ' ');         // remove multiple spaces
    
    if (sSearch == '*' || sSearch == '')
    {
        this.setActive('list');
        return;
    }
    
    /*
     * split on spaces and set up RegExp as OR's between words and wild card
     * anything after each word, (very open search) 
     */
    var aSearch = sSearch.split(/\s/);
    for (var i = 0; i < aSearch.length; i++)
    {
        aSearch[i] = aSearch[i] + '.*';
    }
    oRegExp = new RegExp(aSearch.join('|'), 'i');

    for (var i = 0; i < this.oList.options.length; i++)
    {
        if (oRegExp.test(this.oList.options[i].text))
        {
            this.oSearch.options[this.oSearch.options.length] = new Option(this.oList.options[i].text, this.oList.options[i].value);
        }
    }
    // select the first item in the search if there is one so hitting enter will add that item
    if (this.oSearch.options.length > -1) this.oSearch.selectedIndex = 0;
    
    this.setActive('search');    
}



