﻿// _____________________________________________________________________________
// _____________________________________________________________________________ Namespace(s)
Type.registerNamespace('DanLudwig.Controls.Client');
Type.registerNamespace('Olivehour');



// _____________________________________________________________________________
// _____________________________________________________________________________ TextSelectionStrategy Enum
DanLudwig.Controls.Client.TextSelectionStrategy = function() { };
DanLudwig.Controls.Client.TextSelectionStrategy.prototype = 
{
     Microsoft: 0
    ,W3C: 1
    ,Unknown: 2
}

DanLudwig.Controls.Client.TextSelectionStrategy.registerEnum(
    "DanLudwig.Controls.Client.TextSelectionStrategy", false);



// _____________________________________________________________________________
// _____________________________________________________________________________ AutoCompleteMode Enum
Olivehour.AutoCompleteMode = function() { };
Olivehour.AutoCompleteMode.prototype = 
{
     None: 0
    ,Append: 1
    ,Suggest: 2
    ,SuggestAppend: 3
}

Olivehour.AutoCompleteMode.registerEnum(
    "Olivehour.AutoCompleteMode", false);



// _____________________________________________________________________________
// _____________________________________________________________________________ ComboBoxMode Enum
Olivehour.ComboBoxMode = function() { };
Olivehour.ComboBoxMode.prototype = 
{
     TextBox: 0
    ,DropDownList: 1
}

Olivehour.ComboBoxMode.registerEnum(
    "Olivehour.ComboBoxMode", false);



// _____________________________________________________________________________
// _____________________________________________________________________________ Constructor
DanLudwig.Controls.Client.ComboBox = function(element)
{
    // initialize base (Sys.UI.Control)
    DanLudwig.Controls.Client.ComboBox.initializeBase(this, [element]);

	// DIV containing the entire ComboBox
	this._comboBoxContainer = null;
	
	// TextBox used for input
	this._textBoxControl = null;
	
	// area whose click event triggers the drop down behavior
	this._buttonContainer = null;
	
	// UI button inside the click container
	this._buttonControl = null;
	
	// BulletedList which contains li elements for each item
	this._listControl = null;
	
	// array of ListItems from the BulletedList control
	this._listItems = null;
	
	// AjaxControlToolkit behavior for popups
	this._popupBehavior = null;
	
	// miscellaneous properties
	this._safari3KeyCode = null;
    this._listItemHeight = null;
    this._bestListWidth = null;
    this._bestListHeight = null;
	this._selectedIndex = null;
    this._highlightedIndex = null;
	this._textSelectionStrategy = null;
	this._autoComplete = true;
	this._autoCompleteMode = null;
	this._caseInsensitive = true;
	this._comboBoxMode = false;
    this._listItemHoverStyle = null;
    this._listItemDefaultStyle = null;
    this._highlightSuggestedItem = false;
    this._supressButtonClick = false;
    this._supressFocusHide = true;
    this._autoPostBack = null;
    this._doingPostBack = false;
    
	// event delegates initialized as null
	this.clearDelegates();
}



// _____________________________________________________________________________
// _____________________________________________________________________________ Prototype
DanLudwig.Controls.Client.ComboBox.prototype = 
{
    // _________________________________________________________________________
    // _________________________________________________________________________ Inititalize
    initialize : function() 
    {
        // call base initialize
        DanLudwig.Controls.Client.ComboBox.callBaseMethod(this, 'initialize');
        $common.prepareHiddenElementForATDeviceUpdate();

		// initialize members
        this.initMembers();

		// create delegates
		this.createDelegates();
        
		// initialize Button
		this.initButton();
		
        // initialize ItemList
		this.initList();
		
        // add event handlers
        this.addHandlers();
    }

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Dispose
    dispose : function() 
    {
		// dispose of popup extender
		if (this._popupBehavior)
		{
		    this._popupBehavior.remove_showing(this._popupShowingHandler);
		    this._popupBehavior.remove_shown(this._popupShownHandler);
		    this._popupBehavior.remove_hiding(this._popupHidingHandler);
			this._popupBehavior.dispose();
			this._popupBehavior = null;
		}
		
		// clear event handlers
		this.clearHandlers();
		
		// reset all delegates to null
		this.clearDelegates();

        // call base dispose
        DanLudwig.Controls.Client.ComboBox.callBaseMethod(this, 'dispose');
    }

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Delegate Helpers

    // _________________________________________________________________________
    // ________________________________________________________ Create Delegates|
    createDelegates : function()
    {
		this._listMouseOverHandler = Function.createDelegate(this, this._onListMouseOver);
        this._listMouseDownHandler = Function.createDelegate(this, this._onListMouseDown);
		this._listMouseUpHandler = Function.createDelegate(this, this._onListMouseUp);
		this._listClickHandler = Function.createDelegate(this, this._onListClick);
        this._listDragHandler = Function.createDelegate(this, this._onListDrag);
        this._listSelectStartHandler = Function.createDelegate(this, this._onListSelectStart);
		this._textBoxClickHandler = Function.createDelegate(this, this._onTextBoxClick);
		this._textBoxFocusHandler = Function.createDelegate(this, this._onTextBoxFocus);
		this._textBoxBlurHandler = Function.createDelegate(this, this._onTextBoxBlur);
		this._textBoxKeyPressHandler = Function.createDelegate(this, this._onTextBoxKeyPress);
		this._textBoxKeyDownHandler = Function.createDelegate(this, this._onTextBoxKeyDown);
        this._buttonClickHandler = Function.createDelegate(this, this._onButtonClick);
        this._buttonKeyDownHandler = Function.createDelegate(this, this._onButtonKeyDown);
        this._buttonKeyPressHandler = Function.createDelegate(this, this._onButtonKeyPress);
        this._documentClickHandler = Function.createDelegate(this, this._onDocumentClick);
        this._popupShowingHandler = Function.createDelegate(this, this._popupShowing);
        this._popupShownHandler = Function.createDelegate(this, this._popupShown);
        this._popupHidingHandler = Function.createDelegate(this, this._popupHiding);
    }

,

    // _________________________________________________________________________
    // _________________________________________________________ Clear Delegates|
    clearDelegates : function()
    {
		this._listMouseOverHandler = null;
		this._listMouseDownHandler = null;
		this._listMouseUpHandler = null;
		this._listClickHandler = null;
		this._listDragHandler = null;
		this._listSelectStartHandler = null;
		this._textBoxClickHandler = null;
		this._textBoxFocusHandler = null;
		this._textBoxBlurHandler = null;
		this._textBoxKeyPressHandler = null;
		this._textBoxKeyDownHandler = null;
		this._buttonClickHandler = null;
		this._buttonKeyDownHandler = null;
		this._buttonKeyPressHandler = null;
		this._documentClickHandler = null;
        this._popupShowingHandler = null;
        this._popupShownHandler = null;
        this._popupHidingHandler = null;
    }

,
    
    // _________________________________________________________________________
    // ____________________________________________________________ Add Handlers|
    addHandlers : function()
    {
		// add ItemList event handlers
		$addHandlers(this._listControl, 
		{
			 'mouseover' : this._listMouseOverHandler
			,'mousedown' : this._listMouseDownHandler
			,'mouseup' : this._listMouseUpHandler
			,'click' : this._listClickHandler
			,'drag' : this._listDragHandler
			,'selectstart' : this._listSelectStartHandler
		}, this);

		// add TextBox event handlers
        $addHandlers(this._textBoxControl, 
        {
			 "click" : this._textBoxClickHandler
			,"focus" : this._textBoxFocusHandler
			,"blur" : this._textBoxBlurHandler
			,"keypress" : this._textBoxKeyPressHandler
		}, this);
		if (this.isSafari3OrMore() || this.isInternetExplorer())
    		$addHandler(this._textBoxControl, "keydown", this._textBoxKeyDownHandler);
		
		// add Button event handlers
		$addHandlers(this._buttonControl, 
		{
		     'click' : this._buttonClickHandler
		    ,'keydown' : this._buttonKeyDownHandler
		    ,'keypress' : this._buttonKeyPressHandler
		}, this);
		
		$addHandlers(document, 
		{
			 'click' : this._documentClickHandler
		}, this);
		
    }

,
    
    // _________________________________________________________________________
    // ____________________________________________________________ Add Handlers|
    clearHandlers : function()
    {
		$clearHandlers(this._listControl);
		$clearHandlers(this._textBoxControl);
		$clearHandlers(this._buttonControl);
		$clearHandlers(document);
    }

,
    
    // _________________________________________________________________________
    // ____________________________________________________ Initialize Item List|
	initMembers : function()
	{
        this._buttonContainer = $get(this._buttonControl.parentNode.id);
        this._comboBoxContainer = $get(this._buttonContainer.parentNode.id);
    }

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Initialize Item List

    // _________________________________________________________________________
    // ____________________________________________________ Initialize Item List|
	initList : function()
	{
	    // force inline display on Safari by moving the list to the bottom of the form
	    if (this.isSafari())
	    {
	        this.get_element().removeChild(this._listControl);
	        //document.body.appendChild(this._listControl);
	        //document.forms[0].appendChild(this._listControl);
	        //this.get_element().form.appendChild(this._listControl);
	        var parent = this.get_element().parentNode;
	        while (typeof(parent) != typeof(document.forms[0]))
	        {
	            parent = parent.parentNode
	        }
	        parent.appendChild(this._listControl)

	    }
	
		// apply the correct css styles
		this.initListStyle();
		
		// repopulate the ItemList with ListItems
		this.initListItems();
		
		// now that the ListItems are initialized, set dimensions 
		this.initListDimensions();
		
		// create the popup behavior
		this.initListPopup();
	}

,

    // _________________________________________________________________________
    // ______________________________________________ Initialize Item List Style|
	initListStyle : function()
	{
		// style the ItemList with default skin
        var style = this._listControl.style;
        
        style.display = "block";
        
        // additional margin changes for Safari2
        if (this.isSafari2OrLess())
        {
			style.marginTop = '5px';
        }
	}

,

    // _________________________________________________________________________
    // _________________________________________ Initialize Item List Dimensions|
	initListDimensions : function()
	{
        this._listControl.style.width = this.get_bestListBounds().width + 'px';
        this._listControl.style.width = '0px';
	}

,

    // _________________________________________________________________________
    // ______________________________________________ Initialize Item List Popup|
	initListPopup : function()
	{
		// add the popup behavior to the master container, targeting the ItemList
		this._popupBehavior = $create(AjaxControlToolkit.PopupBehavior, 
		{
			 'id':this.get_id()+'PopupBehavior'
			,'parentElement':this._textBoxControl
			,"positioningMode": AjaxControlToolkit.PositioningMode.BottomLeft
		}, null, null, this._listControl);
		this._popupBehavior.add_showing(this._popupShowingHandler);
		this._popupBehavior.add_shown(this._popupShownHandler);
		this._popupBehavior.add_hiding(this._popupHidingHandler);
		
		// highlight the default item
		if (this._selectedIndex >= 0)
		{
		    this._highlightListItem(this._selectedIndex);
//		    this._textBoxControl.value = this._listItems[this._selectedIndex].text;
		}
		
	    // when selectedIndex is -1, select the first item by default
		else if (this._comboBoxMode == Olivehour.ComboBoxMode.DropDownList)
		{
		    this._highlightListItem(0);
//		    this._textBoxControl.value = this._listItems[0].text;
		}
		
		this._popupShowing();
	}

,

    // _________________________________________________________________________
    // ___________________________________________________ Initialize List Items|
	initListItems : function()
	{
		// there may be more childNodes than items in the list
		this._listItems = new Array();
		var children = this._listControl.childNodes;
        for (var i = 0; i < children.length; i++) 
        {
			// evaluate the current node against a ListItem
            var child = children[i];
            
            // remove non-li elements
            if (child.tagName != 'LI')
            {
				// remove the child, then re-evaluate this index
				this._listControl.removeChild(child);
				i--;
				continue;
            }
            
            // add the item to the internal collection
		    //alert(child.innerHTML.replace(/\r\n/g, " "))
            var item = new Object();
            var text = new String(child.innerHTML);
            if (text.indexOf("\r") >= 0)
            {
                while (text.indexOf("\r") >= 0)
                {
                    text = new String(text.substring(0, text.indexOf("\r"))).trim() 
                        + " " + new String(text.substring(text.indexOf("\r") + 1, text.length)).trim();
                }
            }
            if (text.indexOf("\n") >= 0)
            {
                while (text.indexOf("\n") >= 0)
                {
                    text = new String(text.substring(0, text.indexOf("\n"))).trim() 
                        + " " + new String(text.substring(text.indexOf("\n") + 1, text.length)).trim();
                }
            }

            // internet explorer adds space to the last item
            if (this.isInternetExplorer() && i == children.length - 1 && text != ""
                && text.substring(text.length - 1, text.length) == ' ')
                text = text.substring(0, text.length - 1);
                
            text = text.replace('&amp;', '&');
            text = text.replace('&quot;', '"');
            text = text.replace('&gt;', '>');
            text = text.replace('&lt;', '<');
                
            item.text = new String(text).trim();
            Array.add(this._listItems, item);
			
			// style the ListItem with default skin
            this.initListItemStyle(child);

        }
	}

,

    // _________________________________________________________________________
    // ______________________________________________ Initialize List Item Style|
	initListItemStyle : function(liElement)
	{
		// ensure empty text appears in the list
		liElement._textIsEmpty = false;
		if (liElement.innerHTML.length < 1)
		{
			liElement.innerHTML = '&nbsp;';
			liElement._textIsEmpty = true;
		}
			
		// style the ListItem with default skin
		this._unhighlightListItem(liElement);
	}

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Initialize Button

    // _________________________________________________________________________
    // _______________________________________________________ Initialize Button|
	initButton : function()
	{
		if (this._buttonControl != null)
		{
			// initialize button styles
	        this.initButtonStyle();
			
			// initialize button dimensions
			this.initButtonDimensions();
		}
	}

,

    // _________________________________________________________________________
    // _________________________________________________ Initialize Button Style|
	initButtonStyle : function()
	{
	    var style = this._buttonControl.style;

		// button is display:none initially to reduce screen flicker
        style.visibility = 'visible';
	}

,

    // _________________________________________________________________________
    // ____________________________________________ Initialize Button Dimensions|
	initButtonDimensions : function()
	{
		var style = this._buttonControl.style;
		
		// container height is based on the textbox's clientHeight
		style.height = this._textBoxControl.offsetHeight + 'px';
		
		// Safari 2 gets special height treatment
		if (this.isSafari2OrLess())
		{
			style.height = this._textBoxControl.clientHeight - 4 + 'px';
		}
	}

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Event Handlers

    // _________________________________________________________________________
    // ___________________________________________________________ Popup Showing|
    _popupShowing : function()
    {
        var wBounds = this.getWindowBounds();
        var cBounds = Sys.UI.DomElement.getBounds(this._comboBoxContainer);
        var bBounds = this.get_bestListBounds();
        var safetyDimension = 30;
        
        // determine placement
        var yThreshold = wBounds.y + (wBounds.height / 2);
        var yFlopPoint = cBounds.y + cBounds.height;
        var xThreshold = wBounds.x + (wBounds.width / 2);
        var xFlopPoint = cBounds.x + (cBounds.width / 2);
        
        // vertical alignment & height
        var maxHeight = cBounds.y - wBounds.y;
        var yAlign = 'Top';
        if (yFlopPoint <= yThreshold)
        {
            yAlign = 'Bottom';
            maxHeight = wBounds.height - cBounds.height - maxHeight;
        }
        // when the list contains few enough items, maxHeight may be too large
        var itemHeight = this.get_listItemHeight();
        if (maxHeight >= bBounds.height)
        {
            maxHeight = bBounds.height;
        }
        else
        {
            // adjust maxHeight to be a multiple of listItemHeight
            maxHeight = itemHeight * ((Math.floor(maxHeight / itemHeight)) - 2);
        }
        var visibleItems = maxHeight / itemHeight;
        var yScrollbar = (visibleItems < this._listItems.length);
        var scrollbarWidth = 20;
        
        
        
        // horizontal alignment & width
        var maxWidth = cBounds.x - wBounds.x;
        var xAlign = 'Left';
        if (xFlopPoint <= xThreshold)
        {
            maxWidth = wBounds.width - maxWidth;
        }
        else
        {
            xAlign = 'Right';
            maxWidth = cBounds.width + maxWidth;
        }
        // subtract safety dimension
        maxWidth -= safetyDimension;
        // when the list contains short items, maxWidth may be too large.
        var bestWidth = bBounds.width;
        if (yScrollbar)
        {
            bestWidth += scrollbarWidth;
            if (maxWidth >= bestWidth)
            {
                maxWidth = bestWidth;
            }
        }
        else if (maxWidth >= bestWidth)
        {
            maxWidth = bestWidth;
        }
        
        // set width & height
        var style = this._listControl.style;
        style.height = maxHeight + 'px';
        style.width = maxWidth + 'px';
        
        // account for scrollbars
        if (yScrollbar)
        {
            style.overflow = 'auto';
		    if (this.isSafari())
		    {
                style.overflow = 'scroll';
		    }
		    else if (this.isFirefox1OrLess())
		    {
                style.overflow = '-moz-scrollbars-vertical';
                style.overflowY = 'scroll';
		    }
    		style.overflowX = 'hidden';
        }
        else
        {
            style.overflow = 'hidden';
        }
        
        // set positioning mode
        var bAlign = yAlign + xAlign;
        if (bAlign == 'BottomLeft')
            this._popupBehavior.set_positioningMode(AjaxControlToolkit.PositioningMode.BottomLeft);
        else if (bAlign == 'BottomRight')
            this._popupBehavior.set_positioningMode(AjaxControlToolkit.PositioningMode.BottomRight);
        else if (bAlign == 'TopLeft')
            this._popupBehavior.set_positioningMode(AjaxControlToolkit.PositioningMode.TopLeft);
        else if (bAlign == 'TopRight')
            this._popupBehavior.set_positioningMode(AjaxControlToolkit.PositioningMode.TopRight);
		this._listControl.style.visibility = 'hidden';
    }

,

    // _________________________________________________________________________
    // _____________________________________________________________ Popup Shown|
    _popupShown : function()
    {
		this._listControl.style.visibility = 'hidden';
		
        // reposition after toolkit messes everything up
        var cBounds = Sys.UI.DomElement.getBounds(this._comboBoxContainer);
        var lBounds = Sys.UI.DomElement.getBounds(this._listControl);
        var bBounds = this.get_bestListBounds();
        var y = lBounds.y;
        var x;
        
        if (this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.BottomLeft
            || this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.TopLeft)
        {
            x = cBounds.x;
        }
        else if (this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.BottomRight
            || this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.TopRight)
        {
            x = cBounds.x - (lBounds.width - cBounds.width);
        }
        Sys.UI.DomElement.setLocation(this._listControl, x, y);

        // forcefully hide opera's horizontal scrollbar
		if ((this.isOpera() || this.isSafari2OrLess()) && lBounds.width < bBounds.width
		    && lBounds.height < bBounds.height)
		{
		    var clip = lBounds.height - 18;
		    this._listControl.style.clip = 'rect(auto auto ' + clip + 'px auto)';
		    this._operaBorderBottom = document.createElement('div');
		    // TODO -- fix this to work with styling schema
		    this._operaBorderBottom.style.backgroundColor = 'buttonshadow';
		    if (this.isSafari())
		        this._operaBorderBottom.style.backgroundColor = 'gray';
		    this._operaBorderBottom.style.position = 'absolute';
		    this._operaBorderBottom.style.cursor = 'default';
		    this._operaBorderBottom.style.height = '1px';
		    this._operaBorderBottom.style.width = lBounds.width + 'px';
		    this._listControl.parentNode.insertBefore(this._operaBorderBottom, this._listControl.nextSibling);
		    if (this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.BottomLeft
		        || this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.BottomRight)
		    {
    		    Sys.UI.DomElement.setLocation(this._operaBorderBottom, x, cBounds.y + cBounds.height + clip);
    		}
    		else if (this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.TopLeft
		        || this._popupBehavior.get_positioningMode() === AjaxControlToolkit.PositioningMode.TopRight)
		    {
    		    Sys.UI.DomElement.setLocation(this._listControl, x, y + 17);
    		    Sys.UI.DomElement.setLocation(this._operaBorderBottom, x, cBounds.y - 1);
    		}
		}

        // enforce default scroll
        this._ensureHighlightedIndex();
        this._ensureScrollTop();
        
		this._listControl.style.visibility = 'visible';
    }

,

    // _________________________________________________________________________
    // ____________________________________________________________ Popup Hiding|
    _popupHiding : function()
    {
        if (this._operaBorderBottom != null)
        {
            this._listControl.style.clip = '';
            this._listControl.parentNode.removeChild(this._operaBorderBottom);
            this._operaBorderBottom = null;
        }
        this._highlightSuggestedItem = false;
    }

,

    // _________________________________________________________________________
    // _______________________________________________ On Button Container Click|
    _onButtonClick : function(e)
    {
        if (!this._supressButtonClick)
        {
		    if (this._popupBehavior._visible)
		    {
			    this._popupBehavior.hide();
		    }
		    else
		    {
			    this._popupBehavior.show();
		    }
		}
        
        this._supressButtonClick = false;
		e.preventDefault();
		e.stopPropagation();
		return false;
    }
    
,

    // _________________________________________________________________________
    // _______________________________________________ |
    _onButtonKeyDown : function(e)
    {
        //alert("_onButtonKeyDown: " + e.keyCode);
        if (e.keyCode == Sys.UI.Key.tab
            || e.keyCode == 16)
        {
            return true;
        }
        else
        {
            if (e.keyCode == Sys.UI.Key.enter)
            {
                this._popupBehavior.show();
            }
            else if (e.keyCode == Sys.UI.Key.down 
                || e.keyCode == Sys.UI.Key.up)
            {
                if (this.isSafari3OrMore())
                    this._safari3KeyCode = e.keyCode;
                this._handleArrowKey(e.keyCode);
            }
            
            this._supressButtonClick = true;
		    e.stopPropagation();
		    e.preventDefault();
		    setTimeout("document.getElementById('" + this._textBoxControl.id + "').focus()", '1');
		    return false;
        }

    }
    
,

    // _________________________________________________________________________
    // _______________________________________________ |
    _onButtonKeyPress : function(e)
    {
        //alert("_onButtonKeyPress: " + e.charCode);
        if (e.charCode == Sys.UI.Key.tab
            || e.charCode == 16)
        {
            return true;
        }
	    e.stopPropagation();
	    e.preventDefault();
	    return false;
    }
    
,

    // _________________________________________________________________________
    // ______________________________________________________ On List Mouse Over|
    _onListMouseOver : function(e)
    {
		// do not highlight unless an LI is being hovered
        if (e.target !== this._listControl)
        {
			var target = e.target;
            var children = this._listControl.childNodes;
            
            // loop through children to find a match with the target
            for (var i = 0; i < children.length; ++i)
            {
				// match found, highlight item and break loop
                if (target === children[i])
                {
                    this._highlightListItem(i);
                    //this._highlightedIndex = i;
                    break;
                }  
            }
        }
    }

,

    // _________________________________________________________________________
    // ______________________________________________________ On List Mouse Down|
    _onListMouseDown : function(e)
    {
        if (e.target == this._listControl)
            return true;
        
		e.preventDefault();
		e.stopPropagation();
    }

,

    // _________________________________________________________________________
    // ________________________________________________________ On List Mouse Up|
    _onListMouseUp : function(e)
    {
        if (e.target.tagName == 'scrollbar')
            return true;
        
		// set the TextBox to the highlighted ListItem's text
        if (e.target !== this._listControl)
        {
			var highlightedItem = this._listControl.
				childNodes[this._highlightedIndex];
			var text = this._listItems[this._highlightedIndex].text;
			this._textBoxControl.value = text;

		    // return focus to the TextBox
            this._supressFocusHide = false;
            this._textBoxControl.focus();
		}
        else
        {
            return true;
        }
		e.preventDefault();
		e.stopPropagation();
    }

,

    // _________________________________________________________________________
    // ___________________________________________________________ On List Click|
    _onListClick : function(e)
    {
        if (e.target == this._listControl)
            return true;
        
		// prevent ItemList clicks from triggering document clicks
		e.preventDefault();
		e.stopPropagation();
		return false;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________________ On List Drag|
    _onListDrag : function(e)
    {
		// prevent selection of ListItem text
		e.preventDefault();
		e.stopPropagation();
		return false;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________ On List Select Start|
    _onListSelectStart : function(e)
    {
		// prevent selection of ListItem text
		e.preventDefault();
		e.stopPropagation();
		return false;
    }

,

    // _________________________________________________________________________
    // _______________________________________________________ On Text Box Click|
    _onTextBoxClick : function(e)
    {
		// prevent TextBox clicks from triggering document clicks
		e.preventDefault();
		e.stopPropagation();
		return false;
    }

,

    // _________________________________________________________________________
    // ________________________________________________________ On TextBox Focus|
    _onTextBoxFocus : function(e)
    {
        if (!this._supressFocusHide && this._popupBehavior._visible)
        {
            this._popupBehavior.hide();
            this._supressFocusHide = true;
		    if (this._autoPostBack && this._highlightedIndex != this._selectedIndex
		        && !this._doingPostBack)
		    {
		        this._doingPostBack = true;
		        __doPostBack(this.get_element().id, '');
		    }
        }
    
        this._setTextSelectionRange(
            this._textBoxControl, 
            0, 
            this._textBoxControl.value.length);
//            this._textBoxControl.value.length, 
//            this._textBoxControl.value.length);
//            0, 
//            0);

		e.preventDefault();
		e.stopPropagation();
    }

,

    // _________________________________________________________________________
    // _________________________________________________________ On TextBox Blur|
    _onTextBoxBlur : function(e)
    {
//        if (this._comboBoxMode == Olivehour.ComboBoxMode.DropDownList)
//        {
//            var match = this._matchExactItem();            
//            this._textBoxControl.value = match;
//        }
        this._ensureTextBoxValue();
		this._popupBehavior.hide();
		if (this._autoPostBack && this._highlightedIndex != this._selectedIndex
		    && !this._doingPostBack)
		{
		    this._doingPostBack = true;
		    __doPostBack(this.get_element().id, '');
		}
    }

,

    // _________________________________________________________________________
    // ____________________________________________ Safari 3 On TextBox Key Down|
    _handleArrowKey : function(code)
    {
        if (this.isSafari3OrMore())
            code = this._safari3KeyCode;
        
        else if (this.isSafari2OrLess() && code == 63232)
            code = Sys.UI.Key.up;
        
        else if (this.isSafari2OrLess() && code == 63233)
            code = Sys.UI.Key.down;
        
        if (code == Sys.UI.Key.up || code == Sys.UI.Key.down)
        {            
            if (this._popupBehavior._visible)
            {
                var vector = code - 39;
                if ((vector == -1 && this._highlightedIndex > 0) || (vector == 1 
                    && this._highlightedIndex < this._listItems.length - 1))
                {
                    var newIndex = (this._highlightedIndex + vector);
                    this._textBoxControl.value = this._listItems[newIndex].text;
                    this._highlightListItem(newIndex);
                    this._ensureScrollTop();
                }
            }
            else
            {
                this._popupBehavior.show();
            }
        }
    }

,

    // _________________________________________________________________________
    // ____________________________________________ Safari 3 On TextBox Key Down|
    _onTextBoxKeyDown : function(e)
    {
        if (this.isSafari3OrMore())
        {
            this._safari3KeyCode = e.keyCode;		
        }
        else if (this.isInternetExplorer())
        {
            this._handleArrowKey(e.keyCode);
        }
		return true;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________ On TextBox Key Press|
    _onTextBoxKeyPress : function(e)
    {
        if (e.charCode == 13)
        {
            // if the dropdown is open, close it.
            if (this._popupBehavior._visible)
            {
                if (this._highlightedIndex >= 0)
                    this._textBoxControl.value 
                        = this._listItems[this._highlightedIndex].text
                this._popupBehavior.hide();
		        e.preventDefault();
		        e.stopPropagation();
		        return false;
            }
            
            // allow form submits for free text
            if (this._comboBoxMode == Olivehour.ComboBoxMode.TextBox)
            {
                return true;
            }
            
            // verify textbox value is a valid item from the list
            for (var i = 0; i < this._listItems.length; i++)
            {
                if (this._isExactMatch(
                    this._listItems[i].text, this._textBoxControl.value))
                {
                    return true;
                    break;
                }
            }
            
		    this._ensureTextBoxValue();
		    e.preventDefault();
		    e.stopPropagation();
		    return false;
        }
        
        // IE does not send arrow keypresses to this event
        this._handleArrowKey(e.charCode);
        
        // do not prevent all keypresses, only character keypresses.
        if (this._isAllowedCharCode(e))
        {
            return true;
        }
        
        // get info about the text and selection
        var info = this.get_textSelectionInfo(this._textBoxControl, e);
        var newSelectionStart = info.selectionStart;
        var newSelectionEnd = info.selectionEnd;
        var allText = info.selectionPrefix + info.typedCharacter 
            + info.selectionText.substring(1) + info.selectionSuffix;
        var userText = info.selectionPrefix + info.typedCharacter;
        var suggestedIndex = this._suggestIndex(allText, userText);
        
        // setup suggest & append behaviors
        if (this._autoCompleteMode != Olivehour.AutoCompleteMode.None)
        {
            // setup suggest behavior
            if (this._autoCompleteMode != Olivehour.AutoCompleteMode.Append)
            {
                this._highlightSuggestedItem = true;
                if (!this._popupBehavior._visible)
                    this._popupBehavior.show();
            }
            
            // suggestedIndex can be a valid index or -1.
            if (suggestedIndex >= 0)
            {
                // handle append behavior
                if (this._autoCompleteMode != Olivehour.AutoCompleteMode.Suggest)
                {
                    this._textBoxControl.value = this._listItems[suggestedIndex].text;
                    newSelectionStart = info.selectionStart + 1;
                    newSelectionEnd = this._textBoxControl.value.length;
                }
                // do not append suggestion
                else
                {
                    this._textBoxControl.value = this._listItems[suggestedIndex].text.substring(0, userText.length);
                    newSelectionStart = this._textBoxControl.value.length;
                    newSelectionEnd = this._textBoxControl.value.length;
                }
            }
            
            // when suggestedIndex is -1, there was no match.
            else if (this._comboBoxMode == Olivehour.ComboBoxMode.TextBox)
            {
                this._textBoxControl.value = userText;
                newSelectionStart = userText.length;
                newSelectionEnd = userText.length;
            }
        }
        else if (this._comboBoxMode == Olivehour.ComboBoxMode.TextBox 
            || (this._comboBoxMode == Olivehour.ComboBoxMode.DropDownList
            && suggestedIndex >= 0))
        {
            this._textBoxControl.value = userText;
            newSelectionStart = userText.length;
            newSelectionEnd = userText.length;
        }
        
        this._ensureHighlightedIndex();
        this._ensureScrollTop();
        this._setTextSelectionRange(
            this._textBoxControl, newSelectionStart, newSelectionEnd);

		e.preventDefault();
		e.stopPropagation();
		return false;
    }

,

    // _________________________________________________________________________
    // _______________________________________________________ On Document Click|
    _onDocumentClick : function(e)
    {
		// hide the ItemList
		if (this._popupBehavior._visible)
			this._popupBehavior.hide();
    }
    
,

    // _________________________________________________________________________
    // _________________________________________________________________________ Event Helpers

    // _________________________________________________________________________
    // _____________________________________________________ Highlight List Item|
    _highlightListItem : function(index) 
    {
        //alert("index = '" + index + "'");
        // skip if already highlighted
        if (this._highlightedIndex == index)
            return;
        
        var children = this._listControl.childNodes;
        
        if (index >= 0)
        {
            var liElement = children[index];
            
            // apply hover css class
            if (this._listItemHoverStyle.CssClass.length > 0)
                Sys.UI.DomElement.addCssClass(liElement, this._listItemHoverStyle.CssClass);
            
            // only conditionally apply hover colors if they are set, allowing
            // default colors to cascade into the hover definition.
            var style = liElement.style;
            if (this._listItemHoverStyle.ForeColorHtml.length > 0)
                style.color = this._listItemHoverStyle.ForeColorHtml;
            if (this._listItemHoverStyle.BackColorHtml.length > 0)
                style.backgroundColor = this._listItemHoverStyle.BackColorHtml;
        }
        
		// unselect previously highlighted item
		if (this._highlightedIndex != index 
		    && this._highlightedIndex != null 
		    && this._highlightedIndex >= 0)
		    this._unhighlightListItem(children[this._highlightedIndex]);
        
        // update control state
        this._highlightedIndex = index;
        //liElement._highlighted = true;
    }

,

    // _________________________________________________________________________
    // ___________________________________________________ Unhighlight List Item|
    _unhighlightListItem : function(liElement) 
    {
        // remove hover css class
        if (this._listItemHoverStyle.CssClass.length > 0)
            Sys.UI.DomElement.removeCssClass(liElement, this._listItemHoverStyle.CssClass);

        // remove hover colors by re-applying default colors (or "")
        var style = liElement.style;
        style.color = this._listItemDefaultStyle.ForeColorHtml;
        style.backgroundColor = this._listItemDefaultStyle.BackColorHtml;
    }

,

    // _________________________________________________________________________
    // __________________________________________________ ensureHighlightedIndex|
    _ensureHighlightedIndex : function() 
    {
        // highlight an index according to textbox value
        var textBoxValue = this._textBoxControl.value;
        
        // first, check the current highlighted index
        if (this._highlightedIndex >= 0 
            && this._isExactMatch(this._listItems[this._highlightedIndex].text, textBoxValue))
        {
            return;
        }
        
        // need to find the correct index
        var firstMatch = -1;
        var ensured = false;
        for (var i = 0; i < this._listItems.length; i++)
        {
            var itemText = this._listItems[i].text;
            if (this._isExactMatch(itemText, textBoxValue))
            {
                this._highlightListItem(i);
                ensured = true;
                break;
            }
            
            // if in DropDownList mode, save first match.
            else if (firstMatch < 0 && this._highlightSuggestedItem)
            {
                if (this._isPrefixMatch(itemText, textBoxValue))
                {
                    firstMatch = i;
                }
            }
        }
        
        if (!ensured)
        {
            this._highlightListItem(firstMatch);
        }
    }

,

    // _________________________________________________________________________
    // __________________________________________________ ensureHighlightedIndex|
    _ensureScrollTop : function() 
    {
        // only set position if highlightedindex is not visible
        var itemHeight = this.get_listItemHeight();
        var itemTop = itemHeight * this._highlightedIndex;
        var scrollBottom = this._listControl.scrollTop + this._listControl.clientHeight;
        
        if (itemTop <= this._listControl.scrollTop || itemTop >= scrollBottom)
            this._listControl.scrollTop = 
                this._highlightedIndex * itemHeight;
    }

,

    // _________________________________________________________________________
    // __________________________________________________ ensureHighlightedIndex|
    _ensureTextBoxValue : function() 
    {
        if (this._comboBoxMode == Olivehour.ComboBoxMode.DropDownList)
        {
            var textBoxValue = this._textBoxControl.value;
            var ensured = false;
            
            if (textBoxValue == "")
            {
                for (var i = 0; i < this._listItems.length; i++)
                {
                    if (this._listItems[i].text == "")
                    {
                        this._highlightListItem(i);
                        break;
                    }
                }
            }
            
            if (this._highlightedIndex >= 0)
            {
                ensured = this._isExactMatch(textBoxValue, 
                    this._listItems[this._highlightedIndex].text);
            }
            if (!ensured)
            {
                var suggestedIndex = this._suggestIndex(textBoxValue, textBoxValue);
                if (suggestedIndex >= 0)
                {
                    this._textBoxControl.value = this._listItems[suggestedIndex].text;
                }
                else if (this._highlightedIndex >= 0)
                {
                    this._textBoxControl.value = this._listItems[this._highlightedIndex].text;
                }
                else
                {
                    this._textBoxControl.value = this._listItems[0].text;
                }
            }
        }
    }

,

    // _________________________________________________________________________
    // ____________________________________________________________ isExactMatch|
    _isExactMatch : function(itemText, userText) 
    {
        var exactMatch = (itemText == userText);
        if (!exactMatch && this._caseInsensitive)
            exactMatch = (itemText.toLowerCase() == userText.toLowerCase())

        return exactMatch;
    }

,

    // _________________________________________________________________________
    // ___________________________________________________________ isPrefixMatch|
    _isPrefixMatch : function(itemText, userText) 
    {
        return this._isExactMatch(
            itemText.substring(0, userText.length), userText);
    }

,

    // _________________________________________________________________________
    // ___________________________________________________________ Suggest Index|
    _suggestIndex : function(allText, userText)
    {
        var suggestedIndex = -1;
        var firstMatch = false;
        var exactMatch = false;
        
        for (var i = 0; i < this._listItems.length; i++)
        {
            itemText = this._listItems[i].text;
            
			// skip empty elements
			if (itemText.length < 1)
				continue;
			
			// skip elements that don't match on first character
			if (itemText.substring(0, 1).toLowerCase() != userText.substring(0, 1).toLowerCase())
			    continue;
            
            // first, check if strings are exact match
            var allCandidate = itemText.substring(0, allText.length);
			exactMatch = (allCandidate == allText);
			if (!exactMatch && this._caseInsensitive)
				exactMatch = (allCandidate.toLowerCase() == allText.toLowerCase());
			
			if (exactMatch)
			{
				suggestedIndex = i;
				break;
			}
			else if (!firstMatch)
			{
                var userCandidate = itemText.substring(0, userText.length);
			    firstMatch = (userCandidate == userText);
			    if (!firstMatch && this._caseInsensitive)
				    firstMatch = (userCandidate.toLowerCase() == userText.toLowerCase());
				if (firstMatch)
				    suggestedIndex = i;
			}			
        }
        
        return suggestedIndex;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________________ Suggest Item|
    _suggestItem : function(info)
    {
        var tbNewValue = info.selectionPrefix + info.typedCharacter 
            + info.selectionText.substring(1) + info.selectionSuffix;
        var suggestedItem = null;
        
        for (var i = 0; i < this._listItems.length; i++)
        {
            itemText = this._listItems[i].text;
            
			// skip empty elements
			if (itemText.length < 1)
				continue;

            // setup string segments to be compared
            var liSnippet = itemText.substring(0, tbNewValue.length)
            
            // compare string segments
			var bestMatch = (liSnippet == tbNewValue);
			if (!bestMatch && this._caseInsensitive)
				bestMatch = (liSnippet.toLowerCase() == tbNewValue.toLowerCase());
			
			if (bestMatch)
			{
				suggestedItem = itemText;
				break;
			}
        }
        
        return suggestedItem;
    }

,

    // _________________________________________________________________________
    // ______________________________________________________________ Match Item|
    _matchExactItem : function()
    {
        var exactItem = null;
        var bestMatch = null;
        var tbValue = this._textBoxControl.value;
        for (var i = this._listItems.length - 1; i >= 0 ; i--)
        {
            // setup string segments to be compared
            var itemText = this._listItems[i].text;
            
            // compare string segments
			var exactMatch = (itemText == tbValue);
			if (!exactMatch && this._caseInsensitive)
				exactMatch = (itemText.toLowerCase() == tbValue.toLowerCase());
			
			if (exactMatch)
			{
				return itemText;
				break;
			}
			else
			{
			    if (tbValue.toLowerCase() 
			        == itemText.toLowerCase().substring(0, tbValue.length))
			    {
			        bestMatch = itemText;
			    }
			}
        }
        
        if (bestMatch != null)
            return bestMatch;
        
        // to imitate dropdownlists, default to the top item in the list if not empty.
        if (bestMatch == null && this._listItems[0].text != "")
            bestMatch = this._listItems[0].text;
        
        if (bestMatch == null)
            bestMatch = "";

        return bestMatch;
    }

,

    // _________________________________________________________________________
    // ________________________________________________ Set Text Selection Range|
    _setTextSelectionRange : function(textBox, selectionStart, selectionEnd) 
    {
        var strategy = this.get_textSelectionStrategy();
        if (DanLudwig.Controls.Client.TextSelectionStrategy.Microsoft
            == strategy)
        {
            //alert('createTextRange');
            var range = textBox.createTextRange();
            range.collapse(true);
            range.moveEnd('character', selectionEnd);
            range.moveStart('character', selectionStart);
            range.select();
        }
        else if (DanLudwig.Controls.Client.TextSelectionStrategy.W3C
            == strategy)
        {
            textBox.setSelectionRange(selectionStart, selectionEnd)
        }
    }

,

    // _________________________________________________________________________
    // ____________________________________________________ Is Allowed Char Code|
    _isAllowedCharCode : function(e)
    {
        var charCode = e.charCode;
        
        // return true if keypress actions should be allowed
        if (charCode == 13 || charCode == 27)
        {
            // allow RETURN/ENTER and ESC keys for all browsers
            return true;
        }
        else if (this.isSafari2OrLess())
        {
            // allow special funky apple keypresses
            if (charCode == 63248       // F13 key in Safari 2
                || charCode == 8        // BACKSPACE in Safari 2
                || charCode == 63272    // DELETE in Safari 2
                || charCode == 63276    // PAGEUP in Safari 2
                || charCode == 63277    // PAGEDOWN in Safari 2
                || charCode == 63275    // END in Safari 2
                || charCode == 63273    // HOME in Safari 2
                || charCode == 63234    // ARROWLEFT in Safari 2
                || charCode == 63235    // ARROWRIGHT in Safari 2
                || (charCode >= 63236 && charCode <= 63243)
                                        // FUNCTION keys in Safari 2
                || charCode == 9        // TAB in Safari 2
            )
            {
                //e.preventDefault();
                return true;
            }
        }
        else if (this.isSafari3OrMore())
        {
            // allow special funky apple beta keypresses
            var keyCode = this._safari3KeyCode;
            if (keyCode == 8            // BACKSPACE in Safari 3
                || keyCode == 127       // DELETE in Safari 3
                || keyCode == 9         // TAB in Safari 3
                || keyCode == 45        // INSERT in Safari 3
                || keyCode == 33        // PAGEUP in Safari 3
                || keyCode == 34        // PAGEDOWN in Safari 3
                || keyCode == 35        // END in Safari 3
                || keyCode == 36        // HOME in Safari 3
                || keyCode == 37        // ARROWLEFT in Safari 3
                || keyCode == 39        // ARROWRIGHT in Safari 3
                || keyCode == 145       // SCROLL LOCK Safari 3
                || keyCode == 19        // PAUSE BREAK Safari 3
                || keyCode == 113       // F2 Safari 3
                || keyCode == 115       // F4 Safari 3
                || keyCode == 118       // F7 Safari 3
                || keyCode == 119       // F8 Safari 3
                || keyCode == 120       // F9 Safari 3
                || keyCode == 122       // F11 Safari 3
                || keyCode == 123       // F12 Safari 3
                || keyCode == 91        // WINDOWS LEFT Safari 3
                || keyCode == 92        // WINDOWS RIGHT Safari 3
                || keyCode == 93        // MENU Safari 3
            )
                return true;
        }
        else if (!this.isInternetExplorer())
        {
            // allow kepress events triggered by FF & Opera
            if (charCode == 8           // BACKSPACE in non-microsoft browsers
                || charCode == 46       // DELETE in non-microsoft browsers
                || charCode == 45       // INSERT in non-microsoft browsers
                || charCode == 33       // PAGEUP in non-microsoft browsers
                || charCode == 34       // PAGEDOWN in non-microsoft browsers
                || charCode == 35       // END in non-microsoft browsers
                || charCode == 36       // HOME in non-microsoft browsers
                || charCode == 37       // ARROWLEFT in non-microsoft browsers
                || charCode == 39       // ARROWRIGHT in non-microsoft browsers
                //|| (charCode >= 112 && charCode <= 123)
                                        // FUNCTION keys in non-microsoft browsers
                || charCode == 9        // TAB in non-microsoft browsers
            )
            {
                if (!e.shiftKey)
                    return true;
            }
            else if (charCode == 145)
            {
                // allow SCROLL LOCK events triggered by weird combos
                if (this.isFirefox1OrLess() || this.isOpera())
                {
                    return true;
                }
            }
            else if (charCode == 19)
            {
                // allow PAUSE BREAK events triggered by weird combos
                if (this.isFirefox1Point5() || this.isFirefox2OrMore() || this.isOpera())
                {
                    return true;
                }
            }
            else if (this.isOpera())
            {
                // allow kepress events triggered only by Opera
                if (charCode == 0       // MENU key in Opera
                    || charCode == 16   // SHIFT key in Opera
                    || charCode == 17   // CONTROL key in Opera
                )
                    return true;
            }
            else if (this.isFirefox())
            {
                // allow kepress events triggered only by Firefox
                if (charCode == 91      // WINDOWS LEFT key in Firefox
                    || charCode == 92   // WINDOWS RIGHT key in Firefox
                    || charCode == 93   // MENU key in Firefox
                )
                    return true;
            }
        }
                        
        return false;
    }

,

    // _________________________________________________________________________
    // _________________________________________________________________________ Properties

    // _________________________________________________________________________
    // _________________________________________________________ TextBox Control|
    set_textBoxControl : function(value) 
    {
        if (this._textBoxControl !== value)
        {
            this._textBoxControl = value;
            this.raisePropertyChanged('textBoxControl');
        }
    }
,
    get_textBoxControl : function()
    {
        return this._textBoxControl;
    }

,

    // _________________________________________________________________________
    // __________________________________________________________ Button Control|
	set_buttonControl : function(value) 
	{
	    if (this._buttonControl !== value)
	    {
	        this._buttonControl = value;
	        this.raisePropertyChanged('buttonControl');
	    }
	}
,
	get_buttonControl : function()
	{
	    return this._buttonControl;
	}

,

    // _________________________________________________________________________
    // ____________________________________________________________ List Control|
    set_listControl : function(value) 
    {
        if (this._listControl !== value)
        {
            this._listControl = value;
            this.raisePropertyChanged('listControl');
        }
    }
,
    get_listControl : function()
    {
        return this._listControl;
    }

,

    // _________________________________________________________________________
    // ______________________________________________________ Auto Complete Mode|
    set_autoCompleteMode : function(value) 
    {
        if (this._autoCompleteMode !== value)
        {
            this._autoCompleteMode = value;
            this.raisePropertyChanged('autoCompleteMode');
        }
    }
,
    get_autoCompleteMode : function()
    {
        return this._autoCompleteMode;
    }

,

    // _________________________________________________________________________
    // __________________________________________________________ Combo Box Mode|
    set_comboBoxMode : function(value) 
    {
        if (this._comboBoxMode !== value)
        {
            this._comboBoxMode = value;
            this.raisePropertyChanged('comboBoxMode');
        }
    }
,
    get_comboBoxMode : function()
    {
        return this._comboBoxMode;
    }

,

    // _________________________________________________________________________
    // __________________________________________________________ Selected Index|
    set_selectedIndex : function(value) 
    {
        if (this._selectedIndex !== value)
        {
            this._selectedIndex = value;
            this.raisePropertyChanged('selectedIndex');
        }
    }
,
    get_selectedIndex : function()
    {
        return this._selectedIndex;
    }

,

    // _________________________________________________________________________
    // __________________________________________________________ Auto Post Back|
    set_autoPostBack : function(value) 
    {
        if (this._autoPostBack !== value)
        {
            this._autoPostBack = value;
            this.raisePropertyChanged('autoPostBack');
        }
    }
,
    get_autoPostBack : function()
    {
        return this._autoPostBack;
    }

,

    // _________________________________________________________________________
    // _________________________________________________ List Item Default Style|
    set_listItemDefaultStyle : function(value) 
    {
        if (this._listItemDefaultStyle !== value)
        {
            this._listItemDefaultStyle = value;
            this.raisePropertyChanged('listItemDefaultStyle');
        }
    }
,
    get_listItemDefaultStyle : function()
    {
        return this._listItemDefaultStyle;
    }

,

    // _________________________________________________________________________
    // ___________________________________________________ List Item Hover Style|
    set_listItemHoverStyle : function(value) 
    {
        if (this._listItemHoverStyle !== value)
        {
            this._listItemHoverStyle = value;
            this.raisePropertyChanged('listItemHoverStyle');
        }
    }
,
    get_listItemHoverStyle : function()
    {
        return this._listItemHoverStyle;
    }

,

    // _________________________________________________________________________
    // ______________________________________________________ Selection Strategy|
    get_textSelectionStrategy : function()
    {
        if (this._textSelectionStrategy == null)
        {
            if (this._textBoxControl.createTextRange)
            {
                //alert('createTextRange');
                this._textSelectionStrategy = 
                    DanLudwig.Controls.Client.TextSelectionStrategy.Microsoft;
            }
            else if (this._textBoxControl.setSelectionRange)
            {
                //alert('setSelectionRange');
                this._textSelectionStrategy = 
                    DanLudwig.Controls.Client.TextSelectionStrategy.W3C;
            }
            else
            {
                alert('unknown text selection strategy');
                this._textSelectionStrategy = 
                    DanLudwig.Controls.Client.TextSelectionStrategy.Unknown;
            }
        }
        
        return this._textSelectionStrategy;
    }

,

    // _________________________________________________________________________
    // __________________________________________________________ Selection Info|
    get_textSelectionInfo : function(textBox, e)
    {
        var info = new Object();
        
        info.strategy = this.get_textSelectionStrategy();
        if (DanLudwig.Controls.Client.TextSelectionStrategy.Microsoft
            == info.strategy)
        {
		    var userRange = document.selection.createRange();
		    info.selectionStart = 0;
		    info.selectionEnd = textBox.value.length;
		    while(userRange.moveStart('character', -1) != 0)
		    {
		        info.selectionStart++;
		    }
		    while (userRange.moveEnd('character', 1) != 0)
		    {
		        info.selectionEnd--;
		    }
        }
        else if (DanLudwig.Controls.Client.TextSelectionStrategy.W3C
            == info.strategy)
        {
            info.selectionStart = textBox.selectionStart;
            info.selectionEnd = textBox.selectionEnd;
        }

        info.typedCharacter = String.fromCharCode(e.charCode);
        info.textBoxValue = textBox.value;
        info.selectionPrefix = (info.textBoxValue.length >= info.selectionStart) 
            ? info.textBoxValue.substring(0, info.selectionStart) 
            : '';
        info.selectionText = (info.textBoxValue.length >= info.selectionEnd)
            ? info.textBoxValue.substring(info.selectionStart, info.selectionEnd)
            : '';
        info.selectionSuffix = (info.textBoxValue.length >= info.selectionEnd) 
            ? info.textBoxValue.substring(info.selectionEnd, info.textBoxValue.length)
            : '';
        info.selectionTextFirst = info.selectionText.substring(0, 1);
        
        return info;
    }

,

    // _________________________________________________________________________
    // _________________________________________________________ Get Item Height|
    get_listItemHeight : function()
    {
		if (this._listItemHeight == null && this._listControl.scrollHeight > 0)
		{
			this._listItemHeight = Math.round(this._listControl.scrollHeight / this._listItems.length);
		}
		else if (this.isInternetExplorer6OrLess() 
		    && Math.round(this._listControl.scrollHeight / this._listItems.length) < this._listItemHeight)
		{
			this._listItemHeight = Math.round(this._listControl.scrollHeight / this._listItems.length);
		}
		return this._listItemHeight;
    }

,

    // _________________________________________________________________________
    // _____________________________________________________ Get Best List Width|
    get_bestListWidth : function()
    {
		if (this._bestListWidth == null)
		{
		    var listLeftBorder = 1;
		    var listRightBorder = 1;
		    var leftPadding = 0;
		    var rightPadding = 0;
		    var style = this._listControl.style;
		    
		    
		    // before doing anything, make sure overflow is auto
		    style.overflow = 'auto';
		    
		    // first, default to the clientWidth of the container
		    var bestWidth = this._comboBoxContainer.offsetWidth;
		    
		    // subtract borders from the offsetWidth
		    bestWidth -= (listLeftBorder + listRightBorder);
		    
		    // in order for ths list's scrollWidth to be correct, must set its width
		    var originalWidth = style.width;
		    style.width = bestWidth + 'px';
		    
		    // now compare the list's scrollWidth to the box width
		    if (this._comboBoxContainer.offsetWidth < this._listControl.scrollWidth)
		    {
		        // allow the list to extend as wide as necessary according to its contents
		        bestWidth = this._listControl.scrollWidth + rightPadding + leftPadding;
		    }
		    else if (this.isFirefox() || this.isSafari2OrLess())
            {
                bestWidth -= (listLeftBorder + listRightBorder);
            }

		    // hide the overflow, as there shouldn't be any scrollbars needed now
		    style.overflow = 'hidden';
		    if (this.isSafari2OrLess())
		    {
			    style.overflow = '';
			}
            
            // reset the list's width style to its original setting
            style.width = originalWidth;
            
			this._bestListWidth = bestWidth;
		}
		return this._bestListWidth;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________ Get Best List Height|
    get_bestListHeight : function()
    {
		if (this._bestListHeight == null)
		{
            var bestListHeight = this.get_listItemHeight() * this._listItems.length;
			this._bestListHeight = bestListHeight;
		}
		else if (this.isInternetExplorer6OrLess() 
		    && (this.get_listItemHeight() * this._listItems.length) < this._bestListHeight)
		{
			this._bestListHeight = this.get_listItemHeight() * this._listItems.length;
		}
		return this._bestListHeight;
    }

,

    // _________________________________________________________________________
    // ____________________________________________________ Get Best List Bounds|
    get_bestListBounds : function()
    {
		var bounds = {
		     width: this.get_bestListWidth()
		    ,height: this.get_bestListHeight()
		};
		return bounds;
    }



} // end prototype definition



// _____________________________________________________________________________
// _____________________________________________________________________________ JSON Serialization
//DanLudwig.Controls.Client.ComboBox.descriptor = 
//{
    //properties: [   
        // {name: 'bulletedListItems', type: Array} 
        //,{name: 'property name', type: property type }
    //]
//}



// _____________________________________________________________________________
// _____________________________________________________________________________ Register Class
DanLudwig.Controls.Client.ComboBox.registerClass(
    'DanLudwig.Controls.Client.ComboBox', DanLudwig.Controls.Client.ControlBase);



// _____________________________________________________________________________
// _____________________________________________________________________________ Notify Script Loaded
//if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();