// Copyright (C) 2007-2012 Bristle Software, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 1, or (at your option)
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc.

/******************************************************************************
* com.bristle.jslib.ListBox.js
*******************************************************************************
* Purpose:
*       This file contains utility routines that manipulate HTML "select" 
*       objects and other ListBox type objects.
* Usage:
*       - The typical scenario for using this file from an HTML file is:
*         <script language='JavaScript' src='com.bristle.jslib.ListBox.js'></script>
*         Call the various functions that reside here.
* Assumptions:
*       - The file "com.bristle.jslib.Util.js" has already been loaded.
* Effects:
*       - None.
* Anticipated Changes:
* Notes:
* Implementation Notes:
* Portability Issues:
* Revision History:
*   $Log$
******************************************************************************/

// Create the "namespace" to hold the functions in this file.
com.bristle.jslib.ListBox = {};

/******************************************************************************
* Scroll the selected option of the specified select object into view.
******************************************************************************/
com.bristle.jslib.ListBox.scrollSelectedOptionIntoView = 
function(sel)
{
    com.bristle.jslib.Util.scrollIntoView(sel.options[sel.selectedIndex]);
}

/******************************************************************************
* Get the count of selected items in the specified select box.
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxSelectedCount = 
function(sel)
{
    var intCount = 0;
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].selected)
        {
            intCount++;
        }
    }
    return intCount;
}

/******************************************************************************
* Set the select box to have all items selected or deselected.
*
*@param sel            Select box to operate on.
*@param blnSelected    True to select all; false to deselect all.
*@return               None.
*@throws               None.
******************************************************************************/
com.bristle.jslib.ListBox.setSelectBoxSelectAll = 
function(sel, blnSelected)
{
    for (var i = 0; i < sel.length; i++)
    {
        sel.options[i].selected = blnSelected;
    }
}

/******************************************************************************
* Set the select box to have only one item selected -- the one with the
* specified index.  Optionally, also scroll the selected item into view.
*
*@param sel                Select box to operate on.
*@param intIndex           Index of item to select.
*@param blnScrollIntoView  True to scroll selected item into view;
*                          false otherwise.
*@return                   None.
*@throws com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_INDEX
******************************************************************************/
com.bristle.jslib.ListBox.blnSCROLL_INTO_VIEW = true;
com.bristle.jslib.ListBox.setSelectBoxIndex = 
function(sel, intIndex, blnScrollIntoView)
{
    // Check parameters for errors.
    var blnIndexFound = false;
    for (var i = 0; i < sel.length; i++)
    {
        if (i == intIndex)
        {
            blnIndexFound = true;
            break;
        }
    }
    if (!blnIndexFound)
    {
        throw new com.bristle.jslib.Exception.Exception
                (com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_INDEX,
                 "Index " + intIndex 
                 + " not found in select box " + sel.id
                );
    }

    // Set the specified selection, clearing all other selections.
    for (i = 0; i < sel.length; i++)
    {
        if (i == intIndex)
        {
            sel.options[i].selected = true;
            if (blnScrollIntoView)
            {
                com.bristle.jslib.Util.scrollIntoView(sel.options[i]);
            }
        }
        else
        {
            sel.options[i].selected = false;
        }
    }
}

/******************************************************************************
* Call com.bristle.jslib.ListBox.setSelectBoxIndex if the specified select 
* box is not empty.
******************************************************************************/
com.bristle.jslib.ListBox.setSelectBoxIndexIfSelectBoxNotEmpty =
function(sel, intIndex, blnScrollIntoView)
{
    if (sel.length > 0)
    {
        com.bristle.jslib.ListBox.setSelectBoxIndex
                (sel, intIndex, blnScrollIntoView)
    }
}

/******************************************************************************
* Gets the code values of the selected items in the select box as a
* string of comma-separated values.
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxCodeValue =
function(sel)
{
    // Note:  Must use the value property (the hidden coded value)
    //        which may differ from the display string.  Some callers
    //        rely on this.
    var strValue = "";
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].selected)
        {
            strValue += ((strValue == "") ? "" : ",") + sel.options[i].value;
        }
    }
    return strValue;
}

/******************************************************************************
* Get the zero-based numeric index of the specified code value in the 
* specified select box, or -1 if not found.  Values other than -1 can be 
* used as an index into the options array of the select box.
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxIndexOfCodeValueOrMinusOne =
function(sel, strValue)
{
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].value == strValue)
        {
            return i;
        }
    }
    return -1;
}

/******************************************************************************
* Get the zero-based numeric index of the specified code value in the 
* specified select box.  This value can be used as an index into the options 
* array of the select box.
*@throws com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxIndexOfCodeValue =
function(sel, strValue)
{
    var intIndex 
        = com.bristle.jslib.ListBox.getSelectBoxIndexOfCodeValueOrMinusOne
                                                        (sel, strValue);
    if (intIndex == -1)
    {
        throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE,
             "Code value " + strValue + " not found in select box " + sel.id
            );
    }
    return intIndex;
}

/******************************************************************************
* Set the select box to have only one item selected -- the one with the
* specified code value.  
* Optionally, also scroll the selected item into view.  
* Optionally, add the specified value so that it can be selected.
* Optionally, simulate the "onchange" event of the select box after 
* setting its value.
*
*@param sel                Select box to operate on.
*@param strValue           Code value of item to select.
*@param blnScrollIntoView  True to scroll selected item into view;
*                          false otherwise.
*                          Default: false
*@param blnAddValue        Specifies whether or not to add the value
*                          to the selectbox to prevent the exception
*                          com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE
*                          from being thrown.
*                          Default: false
*@param blnFireChangeEvent Specifies whether or not to fire the onchange
*                          event of the selectbox after setting its value.
*                          Default: false
*@return                   None.
*@throws com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE
*@throws com.bristle.jslib.Exception.intEXC_UNABLE_TO_GENERATE_UNIQUE_VAR_NAME
*                           when unable to generate a unique internal name to 
*                           use in the event firing mechanism.  Only happens
*                           when blnFireChangeEvent is true, and running in a
*                           browsers with no native support for fireEvent()
*                           and only after generating many internal names that
*                           already existed.
******************************************************************************/
com.bristle.jslib.ListBox.blnADD_VALUE         = true;
com.bristle.jslib.ListBox.blnFIRE_CHANGE_EVENT = true;
com.bristle.jslib.ListBox.setSelectBoxCodeValue =
function(sel, 
         strValue, 
         blnScrollIntoView,
         blnAddValue, 
         blnFireChangeEvent
        )
{
    if (blnAddValue)
    {
         com.bristle.jslib.ListBox.addToSelectBox
                (sel,
                 strValue,
                 strValue)
    }
    else
    {
        // Check that the specified value exists in the select box.
        // Note: May throw exception:
        //    com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE
        com.bristle.jslib.ListBox.getSelectBoxIndexOfCodeValue(sel, strValue);
    }

    // Set the specified selection, clearing all other selections.
    for (var i = 0; i < sel.length; i++)
    {
        sel.options[i].selected = (sel.options[i].value == strValue);
        //?? Is the above necessary, now that we do the following?
        if (sel.options[i].value == strValue)
        {  
            sel.selectedIndex = i;
            if (blnScrollIntoView)
            {
                com.bristle.jslib.Util.scrollIntoView(sel.options[i]);
            }
            break;
        }
    }

    // Note:  Must call com.bristle.jslib.Event.fireEvent("onclick"), not 
    //        click(), to get the onclick event to fire.  Calling click() 
    //        directly for either the select box or the selected option, 
    //        as shown below, resets the value of the select box to the 
    //        first option.  Why?
    //            sel.click();
    //        or:
    //            sel.options[sel.selectedIndex].click();
    // Note:  If calling com.bristle.jslib.Event.fireEvent("onclick") for 
    //        the select box didn't work, the next possibility would have 
    //        been to call it for the currently selected option.
    // Note:  Since we moved from the "onclick" event to "onchange", the 
    //        above notes are irrelevant.  Keep them in case we ever 
    //        have to move back to "onclick".
    if (blnFireChangeEvent)
    {
        com.bristle.jslib.Event.fireEvent
                (sel, 
                 "onchange", 
                 com.bristle.jslib.Event.blnFIRE_EVENT_EVEN_IF_DISABLED);
    }
}

/******************************************************************************
* Set the select box to have an additional item selected or deselected,
* without affecting the selection of other values.
******************************************************************************/
com.bristle.jslib.ListBox.setSelectBoxAdditionalCodeValue =
function(sel, strValue, blnSelected)
{
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].value == strValue)
        {
            sel.options[i].selected = blnSelected;
        }
    }
}

/******************************************************************************
* Get the display values of the selected items in the select box as a
* string of comma-separated values.
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxDisplayValue =
function(sel)
{
    // Note:  Must return the text property (the display string) which
    //        may differ from the hidden coded value.  Some callers
    //        rely on this.
    var strValue = "";
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].selected)
        {
            strValue += ((strValue == "") ? "" : ",") + sel.options[i].text;
        }
    }
    return strValue;
}

/******************************************************************************
* Get true or false, indicating whether the specified code value is currently
* selected in the specified select box.
******************************************************************************/
com.bristle.jslib.ListBox.getSelectBoxCodeValueIsSelected =
function(sel, strValue)
{
    for (var i = 0; i < sel.length; i++)
    {
        if (sel.options[i].selected && (sel.options[i].value == strValue))
        {
            return true;
        }
    }
    return false;
}

/******************************************************************************
* Clear the specified select box.
******************************************************************************/
com.bristle.jslib.ListBox.clearSelectBox =
function(sel)
{
    sel.length = 0;
}

/******************************************************************************
* Add the specified item (text and value) to the specified select box.
*
*@param sel                 Select box to operate on.
*@param strText             Display value of item.
*@param strValue            Code value of item.
*@return                    None.
*@throws                    None.
******************************************************************************/
com.bristle.jslib.ListBox.addToSelectBox =
function(sel, strText, strValue)
{
    var optNew = document.createElement("OPTION");
    optNew.text = " " + strText + " ";
    optNew.value = strValue;
    sel.options.add(optNew);
}

/******************************************************************************
* Add the specified item (text and value) to the specified select box,
* and make it the selected item. Optionally, also scroll the selected 
* item into view.
*
*@param sel                 Select box to operate on.
*@param strText             Display value of item.
*@param strValue            Code value of item.
*@param blnScrollIntoView   True to scroll selected item into view;
*                           false otherwise.
*@return                    None.
*@throws                    None.
******************************************************************************/
com.bristle.jslib.ListBox.addToSelectBoxAndSelect =
function(sel, strText, strValue, blnScrollIntoView)
{
    // Note: No need to catch 
    //       com.bristle.jslib.Exception.intEXC_NO_SUCH_SELECT_BOX_CODE_VALUE.
    //       We add the value and then select it, so it must be there.
    // Note: No need to catch
    //       com.bristle.jslib.Exception.intEXC_UNABLE_TO_GENERATE_UNIQUE_VAR_NAME.
    //       It can only occur when com.bristle.jslib.ListBox.blnFIRE_CHANGE_EVENT
    //       is passed.
    com.bristle.jslib.ListBox.addToSelectBox (sel, strText, strValue);
    com.bristle.jslib.ListBox.setSelectBoxCodeValue
                    (sel, 
                     strValue, 
                     blnScrollIntoView,
                     !com.bristle.jslib.ListBox.blnADD_VALUE,
                     !com.bristle.jslib.ListBox.blnFIRE_CHANGE_EVENT);
}


