/************************************************************************
 * DropDownCheckList.js
 * 
 *  Client-side javascript to support the ASP.NET DropDownCheckList 
 *  server control.
 *
 *  written by Mike Ellison, 20-September-2005
 *
 ************************************************************************
 *  Copyright (c) 2005 Mike Ellison.  All rights reserved.
 *  
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met:
 *  
 *    * Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions, the following disclaimer, and the following
 *      acknowledgements. 
 *  	
 *    * Redistributions in binary form must reproduce the above copyright notice, 
 *      this list of conditions and the following disclaimer in the documentation 
 *  	and/or other materials provided with the distribution.
 *  	
 *    * The name of the author may not be used to endorse or promote products 
 *      derived from this software without specific prior written permission. 
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED 
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
 *  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  Acknowledgements:
 *      - The "shim" technique used to support Internet Explorer 6.x
 *        is courtesy Joe King of Coalesys, Inc., 
 *        http://dotnetjunkies.com/WebLog/jking/archive/2003/07/21/488.aspx
 *
 *      - Javascript functions findPosX and findPosY for locating objects absolutely
 *        are courtesy Peter-Paul Koch, http://www.quirksmode.org
 *  
 ************************************************************************/

/************************************************************************
 * Utility Functions (global to the page)
 ************************************************************************/
 var DDCL_DROPDOWNMODE_INLINE        = 0;
 var DDCL_DROPDOWNMODE_ONTOP         = 1;
 var DDCL_DROPDOWNMODE_ONTOPWITHSHIM = 2;
 var DDCL_DISPLAYTEXTLIST_LABELS     = 0;
 var DDCL_DISPLAYTEXTLIST_VALUES     = 1;
 
 ddcl_Objects = new Array();
 function DDCL_GetObject(id)
 {
   for (var i=0; i<ddcl_Objects.length; i++)
   {
     // alert(id.substring(ddcl_Objects[i].length) + ", " + ddcl_Objects[i].id);
     if (id.substring(0,ddcl_Objects[i].id.length) == ddcl_Objects[i].id) 
     {
        // alert('gotit');
        return ddcl_Objects[i];
     }
   }
   return null;
 }
 
 // -----------------------------------------------------
 function DDCL_findPosX(obj)
{
    /***
    Original script by Peter-Paul Koch, http://www.quirksmode.org
    ***/
	var curleft = 5;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

function DDCL_findPosY(obj)
{
    /***
    Original script by Peter-Paul Koch, http://www.quirksmode.org
    ***/
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;
	return curtop;
}
 // -----------------------------------------------------
 
 
 function DDCL_HandleCheckboxClickById(id)
 {
    var obj = DDCL_GetObject(id);
    obj.HandleCheckboxClick();
 }

 // called as the onclick event handler for a check box
 function DDCL_HandleCheckboxClick(e)
 {
    if (!e) var e = window.event;
    if (e) {
        var elem = this;
        if (elem) {
           var obj = DDCL_GetObject(elem.id);
           if (obj)
           {
              obj.HandleCheckboxClick(); 
              obj.inCheckboxDiv = true;
           }
        }
    }    
 }

 
 // called as the onclick event handler for a display box
 function DDCL_HandleDisplayBoxClick(e)
 {
    if (!e) var e = window.event;
    if (e) {
        var elem = this;
        if (elem) {
           var obj = DDCL_GetObject(elem.id);
           if (obj)
           {
              obj.HandleDisplayBoxClick(); 
              obj.inCheckboxDiv = true;
           }
        }
    }    
 }
 
 // called as the onclick event handler for a checkbox div
 function DDCL_HandleCheckboxDivClick(e)
 {
    if (!e) var e = window.event;
    if (e) {
       var obj=DDCL_GetObject(this.id);
       if (obj)
        obj.inCheckboxDiv = true;
    }    
 }
  
 function DDCL_HandleDocumentClick(id)
 {   
   var obj = DDCL_GetObject(id);
   if (obj)
   {
    if (obj.inCheckboxDiv == true)
        obj.inCheckboxDiv = false;
    else
        obj.CloseCheckList(); 
   }
 }
 

/************************************************************************
 * Object definition
 ************************************************************************/
 // constructor; takes a required id as a parameter; this will be the
 // same as the ClientID of the ASP.NET control
 function DDCL_DropDownCheckList(id, textWhenNone, separator, truncateString, dropDownMode, allowExpand, displayList)
 {
    // remember the id, displayMode, and text to display when no
    // boxes are checked
    this.id = id;
    this.textWhenNone = textWhenNone;
    this.separator = separator;
    this.truncateString = truncateString;
    this.dropDownMode = dropDownMode;
    this.allowExpand = allowExpand;
    this.displayList = displayList;
    
    // get the elements representing the display box div and
    // checklist div; these will be referenced by object methods
    this.divCheckboxes  = document.getElementById(id + "_checkboxes");
    this.divBoundingBox = document.getElementById(id + "_boundingbox");
    this.divText        = document.getElementById(id + "_text");
    this.divDisplayBox  = document.getElementById(id + "_displaybox");
    this.img            = document.getElementById(id + "_img");
    this.shim           = document.getElementById(id + "_shim");
    
    this.boundingBoxWidth = this.divBoundingBox.offsetWidth;
    
    this.divCheckboxes.style.display = "none";
    
    // wire click events for the display box div and the checkboxes div
    this.divDisplayBox.onclick = DDCL_HandleDisplayBoxClick;
    this.divCheckboxes.onclick = DDCL_HandleCheckboxDivClick;
    
    // wire click events for the checkboxes
    var e = this.divCheckboxes.getElementsByTagName("input");
    for (var i=0; i<e.length; i++)
    {
      if (e[i].type == "checkbox")
      {
        e[i].onclick = DDCL_HandleCheckboxClick;
      }
    }
           
    // if the browser supports bubbling events, install a default click
    // handler for the document too, that will close the checkboxes div
    // if there is a click outside it
    if (document.attachEvent)
    {
        document.attachEvent('onclick'
                              , function() { eval("DDCL_HandleDocumentClick('" + id + "');") }
                             );
    }
    else if (document.addEventListener)
    {
        document.addEventListener('click'
                              , function() { eval("DDCL_HandleDocumentClick('" + id + "');") }
                              , false);  
    }    
        
    
    // initial display of checked items, or the textWhenNone string
    this.DisplayCheckedItems();
 
    // add this object to our reference array on the page so we can get it 
    // back later
    ddcl_Objects.push(this);

 }
 
 
 // get a label given a checkbox element
 function DDCL_DropDownCheckList_GetLabelForCheckbox(elem)
 {
   var e = this.divCheckboxes.getElementsByTagName("label");
   for (var i=0; i<e.length; i++)
   {
     if (e[i].htmlFor == elem.id)
     {
        for (var j=0; j<e[i].childNodes.length; j++)
        {
            if (e[i].childNodes[j].nodeType == 3) //text type
            {
                return e[i].childNodes[j].nodeValue;
            }
        }
     }
   }
   
   // still here?  no <label> for this checkbox then
   return null;
 }

 
 // refresh the checked items listing in the display box
 function DDCL_DropDownCheckList_DisplayCheckedItems()
 {
    var sLabel = "";
    var sCurrent = "";
    var sFull = "";
    var sBefore = "";
    var sCompText = "";
    var bEllipsisAdded = false;
    
    // get all checkboxes in the checklist
    var e = this.divCheckboxes.getElementsByTagName("input");
    // clear the display text
    this.divText.innerHTML = "";       
    // clear the title (tooltip) attribute
    this.divDisplayBox.title = "";
         
    // loop through all checkboxes in the checklist to see 
    // which ones are checked;
    for (var i=0; i<e.length; i++)
    {
      if (e[i].type == "checkbox" && e[i].checked)
      {
        // if the checkbox is checked, get its associated label text
        if (this.displayList == DDCL_DISPLAYTEXTLIST_LABELS)
            // get the label for the checkbox
            sLabel = this.GetLabelForCheckbox(e[i]);        
        else
            // get the value for the checkbox
            sLabel = e[i].value;
        
        // add the list separator if necessary
        if (sCurrent != "") 
        {   
            sCurrent += this.separator;
            sFull += this.separator;
        }
        
        sFull += sLabel;
        sCurrent += sLabel;
        
       
        if (bEllipsisAdded == false)
        {
            // add this one to the text box, then test for the
            // width against the display box
            this.divText.innerHTML = "<nobr>" + sCurrent + "</nobr>";
            if (this.divText.offsetWidth > this.boundingBoxWidth && this.allowExpand == false)
            {
              // too big; shrink by what we can and add the ellipsis (or other trunacte string)
              while (this.divText.offsetWidth > this.boundingBoxWidth && sCurrent.length > 0)
              {
                sCurrent = sCurrent.substr(0, sCurrent.length - 1);
                this.divText.innerHTML = "<nobr>" + sCurrent + this.truncateString + "</nobr>";                
              }
              
              // and indicate the ellipsis (or other truncate text) has been added
              bEllipsisAdded = true;
            }
        }
      }
    }
    
    // finally, if there are no contents, display the textWhenNone message
    if (this.divText.innerHTML == "") 
    {
        if(this.textWhenNone == "")
            this.divText.innerHTML = "&nbsp;";
        else
           this.divText.innerHTML = this.textWhenNone;
    }
    
    // if we added the ellipsis, set the title attribute to the full string
    // (which will display as a tooltip in most browsers)
    if (bEllipsisAdded)
        this.divDisplayBox.title = sFull;
    else
        this.divDisplayBox.title = "";
   
  }
 
 // handle a click in a participating checkbox
 function DDCL_DropDownCheckList_HandleCheckboxClick()
 {
    // refresh the checked items display box
    this.DisplayCheckedItems();
 }
 
 // handle a click in the display box
 function DDCL_DropDownCheckList_HandleDisplayBoxClick()
 {    
    // toggle the display of the checklist
    this.ToggleCheckList();
 }

 // toggle the display of the checklist boxes
 function DDCL_DropDownCheckList_ToggleCheckList()
 {
    if (this.divCheckboxes.style.display != "none")
        this.CloseCheckList();
    else
        this.OpenCheckList();                    
 }
 
 // explicitly close the checklist
 function DDCL_DropDownCheckList_CloseCheckList()
 {
    // hide the checkboxes div
    this.divCheckboxes.style.display = "none";
    
    // if we're using a shim, hide it as well
    if (this.dropDownMode == DDCL_DROPDOWNMODE_ONTOPWITHSHIM)
        this.shim.style.display = "none";
 }
 
 // explicitly open the checklist
 function DDCL_DropDownCheckList_OpenCheckList()
 {
    // open the checkboxlist; first, position it below the displaybox

    // determine the position based on the dropDownMode
    if (this.dropDownMode == DDCL_DROPDOWNMODE_INLINE)
    {
        // inline mode; we're already setup as we need to be
        this.divCheckboxes.style.display = "block";
    }
    else
    {
        // on top modes; position the box
        this.divCheckboxes.style.left = DDCL_findPosX(this.divDisplayBox);
        this.divCheckboxes.style.top  = DDCL_findPosY(this.divDisplayBox) + this.divDisplayBox.offsetHeight;
        
        this.divCheckboxes.style.display = "block";
       // if we want the shim, apply that now
        if (this.dropDownMode == DDCL_DROPDOWNMODE_ONTOPWITHSHIM)
        {
            this.shim.style.width   = this.divCheckboxes.offsetWidth;
            this.shim.style.height  = this.divCheckboxes.offsetHeight;
            this.shim.style.top     = this.divCheckboxes.style.top;
            this.shim.style.left    = this.divCheckboxes.style.left;
            this.shim.style.zIndex  = this.divCheckboxes.style.zIndex - 1;
            this.shim.style.display = "block";                
        }

        
    }
 }
 
 // wire methods as prototypes
 DDCL_DropDownCheckList.prototype.DisplayCheckedItems     = DDCL_DropDownCheckList_DisplayCheckedItems;
 DDCL_DropDownCheckList.prototype.GetLabelForCheckbox     = DDCL_DropDownCheckList_GetLabelForCheckbox;
 DDCL_DropDownCheckList.prototype.HandleCheckboxClick     = DDCL_DropDownCheckList_HandleCheckboxClick;
 DDCL_DropDownCheckList.prototype.HandleDisplayBoxClick   = DDCL_DropDownCheckList_HandleDisplayBoxClick;
 DDCL_DropDownCheckList.prototype.ToggleCheckList         = DDCL_DropDownCheckList_ToggleCheckList;
 DDCL_DropDownCheckList.prototype.CloseCheckList          = DDCL_DropDownCheckList_CloseCheckList;
 DDCL_DropDownCheckList.prototype.OpenCheckList           = DDCL_DropDownCheckList_OpenCheckList;
