﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

// This behavior can be attached to a textbox to enable auto-complete/auto-suggest
// scenarios.

AjaxControlToolkit.SelectTextBoxBehavior = function(element) 
{
    /// <param name="element" domElement="true">The DOM element the behavior is associated with.</param>
    AjaxControlToolkit.SelectTextBoxBehavior.initializeBase(this, [element]);
    this._servicePath = null;
    this._serviceMethod = null;
    this._minimumPrefixLength = 3;
    this._completionSetCount = 10;
    this._completionInterval = 1000;        
    this._completionListElementID = null;
    this._completionListElement = null;
    this._popupBehavior = null;
    this._timer = null;
    this._cache = null;
    this._currentPrefix = null;
    this._selectIndex = -1;
    this._keyDownHandler = null;
    this._itemClickHandler = null;
    this._tickHandler = null;
    this._enableCaching = true;
    this._autoHide = false;
    this.HiddenFieldID = null;
    this.ListWidth = "200px";
    this.ListItemClass = null;
    this.ListClass = null;
    this.ListLinkClass = null;
    this.HoverPopup = null;
    this.ItemSelectedClientHandler = null;
}

AjaxControlToolkit.SelectTextBoxBehavior.prototype = 
{
    
    /// <summary>
    /// Constructor
    /// </summary>
    initialize: function() 
    {
        AjaxControlToolkit.SelectTextBoxBehavior.callBaseMethod(this, 'initialize');
        this._tickHandler = Function.createDelegate(this, this._onTimerTick);
        this._keyDownHandler = Function.createDelegate(this, this._onKeyDown);
        this._itemClickHandler = Function.createDelegate(this, this._itemClick);

        this._timer = new Sys.Timer();
        this.initializeTimer(this._timer);
        
        var element = this.get_element();
        this.initializeTextBox(element);
        
        // CHECK IF COMPLETION LIST ELEMENT ID IS DEFINED
        if(this._completionListElementID !== null)
        {
            // COMPLETION LIST ELEMENT ID IS DEFINED... USE THE ONE SPECIFIED
            this._completionListElement = $get(this._completionListElementID);
        }
        
        
        if (this._completionListElement == null ) 
        {
            // COMPLETION LIST ELEMENT ID IS NOT DEFINED... CREATE A DIV TO USE
            this._completionListElement = document.createElement('DIV');
            this._completionListElement.id = this.get_id() + '_completionListElem';

            // Safari styles the element incorrectly if it's added to the desired location
            if (Sys.Browser.agent === Sys.Browser.Safari) 
            {
                document.body.appendChild(this._completionListElement);
            } 
            else 
            {
                element.body.appendChild(this._completionListElement);
            }
        }
        // INITIALIZE THE COMPLETION LIST
        this.initializeCompletionList(this._completionListElement);
        
        if(this.HoverPopup)
        {
            this._popupBehavior = 
                 $create(AjaxControlToolkit.PopupBehavior, { 'id':this.get_id()+'PopupBehavior', 'parentElement':element, "positioningMode": AjaxControlToolkit.PositioningMode.BottomLeft }, null, null, this._completionListElement);
        }
    },
    
    get_completionInterval: function() 
    {
        /// <value type="Number">Auto completion timer interval in milliseconds.</value>
        return this._completionInterval;
    },
    
    set_completionInterval: function(value) 
    {
        this._completionInterval = value;
    },
    
    
    get_autoHide: function() 
    {
        return this._autoHide;
    },
    
    set_autoHide: function(value) 
    {
        this._autoHide = value;
    },
    
    
    
    get_completionList: function() 
    {
        /// <value domElement="true">List dom element.</value>
        return this._completionListElement;
    },
    
    set_completionList: function(value) 
    {
        this._completionListElement = value;
    },
    
    get_completionSetCount: function() 
    {
        /// <value type="Number">Maximum completion set size.</value>
        return this._completionSetCount;
    },
    set_completionSetCount: function(value) 
    {
        this._completionSetCount = value;
    },
    
    get_minimumPrefixLength: function() 
    {
        /// <value type="Number">Minimum text prefix length required to perform behavior.</value>
        return this._minimumPrefixLength;
    },
    set_minimumPrefixLength: function(value) 
    {
        this._minimumPrefixLength = value;
    },
    
    
    get_itemSelectedClientHandler: function() 
    {
        /// <value type="Number">Minimum text prefix length required to perform behavior.</value>
        return this._itemSelectedClientHandler;
    },
    set_itemSelectedClientHandler: function(value) 
    {
        this._itemSelectedClientHandler = value;
    },
    
    get_serviceMethod: function() 
    {
        /// <value type="String">Web service method.</value>
        return this._serviceMethod;
    },
    set_serviceMethod: function(value) 
    {
        this._serviceMethod = value;
    },
    
    get_servicePath: function() 
    {
        /// <value type="String">Web service url.</value>
        return this._servicePath;
    },
    set_servicePath: function(value) 
    {
        this._servicePath = value;
    },
    
    get_enableCaching: function() 
    {
        /// <value type="Boolean">Get or sets whether suggestions retrieved from the webservice should be cached.</value>
        return this._enableCaching;
    },
    set_enableCaching: function(value) 
    {
        this._enableCaching = value;
    },
    
    get_completionListElementID: function()
    {
        /// <value type="String>ID of the completion div element. </value>
        return this._completionListElementID;
    },
    set_completionListElementID: function(value) 
    {
        this._completionListElementID = value;  
    },    
    
    
    set_ItemSelectedClientHandler: function(value)
    {
        this.ItemSelectedClientHandler = value;
    },
    
    
    get_ItemSelectedClientHandler: function()
    {
        return this.ItemSelectedClientHandler;
    },
    
    
    /// <summary>
    /// Cleans up the resources used by this control
    /// </summary>
    dispose: function() 
    {
        /// KILL OFF POPUP BEHAVIOR ELEMENT
        if (this._popupBehavior) 
        {
            this._popupBehavior.dispose();
            this._popupBehavior = null;
        }
        // KILL OFF TIMER ELEMENT
        if(this._timer) 
        {        
            this._timer.dispose();
            this._timer = null;
        }
        var element = this.get_element();
        // KILL OFF HANDLERS FOR TARGET ELEMENT
        if(element) 
        {
            $removeHandler(element, "keydown", this._keyDownHandler);
        }
        // KILL OFF ALL OTHER HANDLERS
        this._tickHandler = null;
        this._keyDownHandler = null;
        
        // CALL BASE ELEMENT DISPOSE CONTROL
        AjaxControlToolkit.SelectTextBoxBehavior.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Sets up the timer control
    /// </summary>
    initializeTimer: function(timer) 
    {
        timer.set_interval(this._completionInterval);
        timer.add_tick(this._tickHandler);
    },
    
    /// <summary>
    /// Sets up the text box control
    /// </summary>
    initializeTextBox: function(element) 
    {
        element.autocomplete = "off";
        $addHandler(element, "keydown", this._keyDownHandler);
    },
    
    /// <summary>
    /// Sets up the completion list control
    /// </summary>
    initializeCompletionList: function(element) 
    {
        var completionListStyle = element.style;
        completionListStyle.visibility = 'hidden';
        //completionListStyle.backgroundColor = this._textBackground;
        completionListStyle.color = this._textColor;
        completionListStyle.cursor = 'default';
        completionListStyle.unselectable = 'unselectable';
        completionListStyle.overflow = 'hidden';
        completionListStyle.width = '200px';
    },
    
    /// <summary>
    /// Shows the completion list
    /// </summary>
    _showCompletionList: function()
    {
        if(this.HoverPopup)
            this._popupBehavior.show();
        else
        {
            this._completionListElement.style.display = 'block';
            this._completionListElement.style.visibility = 'visible';
        }
    },
    
    /// <summary>
    /// Hides the completion list
    /// </summary>
    _hideCompletionList: function()
    {
        if(this.HoverPopup)
            this._popupBehavior.hide();
        else
        {
            this._completionListElement.style.display = 'none';
            this._completionListElement.style.visibility = 'hidden';
        }    
        this._completionListElement.innerHTML = '';
    },
    
    /// <summary>
    /// Fires when a key is pressed in the target text box
    /// </summary>
    _onKeyDown: function(ev) 
    {
        var k = ev.keyCode ? ev.keyCode : ev.rawEvent.keyCode;
        this._timer.set_enabled(true);
    },
    
    /// <summary>
    /// Fires when the xml http request completes
    /// </summary>
    _onMethodComplete: function(result, context, methodName) 
    {
        this._update(context, result, /* cacheResults */ true);
    },
    
    /// <summary>
    /// Fires when the xml http request fails
    /// </summary>
    _onMethodFailed: function(err, response, context) 
    {
        // no op
    },
    
    /// <summary>
    /// Fires when the timer ticks
    /// </summary>
    _onTimerTick: function(sender, eventArgs) 
    {
        // CHECK IF A SERVICE PATH AND SERVICE METHOD ARE DEFINED
        if (this._servicePath && this._serviceMethod) 
        {
            var text = this.get_element().value;
            // CHECK IF TEXT IS LESS THAN THE MINIMUM PREFIX LENGTH
            if (text.trim().length < this._minimumPrefixLength) 
            {
                // TEXT IS LESS THAN MINIMUM LENGTH... DO NOTHING
                this._currentPrefix = null;
                this._update('', null, /* cacheResults */ false);
                return;
            }
            
            // CHECK IF CURRENT TEXT ISNT EQUAL TO ITSELF
            if (this._currentPrefix !== text)
            {
                this._currentPrefix = text;
                // CHECK IF CACHING IS SET AND IF CURRENT CACHE EXISTS
                if (this._cache && this._cache[text]) 
                {
                    // DISPLAY CACHED RESULTS
                    this._update(text, this._cache[text], false);
                    return;
                }
                
                // THIS IS WHERE THE XML HTTP REQUEST GETS CALLED
                Sys.Net.WebServiceProxy.invoke(this.get_servicePath(), this.get_serviceMethod(), false,
                                            { prefixText : this._currentPrefix, count: this._completionSetCount },
                                            Function.createDelegate(this, this._onMethodComplete),
                                            Function.createDelegate(this, this._onMethodFailed),
                                            text);
            }
        }
    },
    
    
    /// <summary>
    /// 
    /// </summary>
    _setValue: function(text) 
    {
        this._timer.set_enabled(false);
       $get(this.HiddenFieldID).value = text;
    },	
    
    /// <summary>
    /// Fires when a radio button is clicked
    /// </summary>
    _itemClick: function(hiddenFieldId, itemSelectedClickHandler, title, textBox, completionListId, autoHide)
    {
        // GET ALL THE RADIO BUTTONS IN THE LIST
        var radioButtons = document.getElementsByName('selectTextBoxItem');
        
        var chosenValue = "";
        
        // LOOP OVER EACH RADIO BUTTON
        for(var i = 0; i < radioButtons.length; i++)
        {
            // CHECK IF IT IS CLICKED
            if(radioButtons[i].checked)
            {
				chosenValue = radioButtons[i].value;
				// CHECK IF WERE AUTO HIDING THIS BOX
				if(autoHide)
				{
					// SET VALUE OF HIDDEN FIELD TO THE VALUE OF SELECTED RADIO BUTTON
					document.getElementById(hiddenFieldId).value = chosenValue;
					// SET TEXT OF THE TEXTBOX TO THE TEXT OF THE SELECTED RADIO BUTTON
					document.getElementById(textBox).value = title;
					// HIDE THE COMPLETION LIST BOX
					document.getElementById(completionListId).style.display = 'none';
                }
            }
        }
        
        if (itemSelectedClickHandler == 'null')
			return;
        
       eval(itemSelectedClickHandler + "(chosenValue)");
			
    },
    
    
    
    /// <summary>
    /// 
    /// </summary>
    _update: function(prefixText, completionItems, cacheResults) 
    {
        this._timer.set_enabled(false);
        if (cacheResults && this.get_enableCaching()) 
        {
            if (!this._cache) {
                this._cache = {};
            }
            this._cache[prefixText] = completionItems;
        }

        this._completionListElement.innerHTML = '';
        
        // IF THE COMPLETION LIST IS RETURNED CORRECTLY...
        if (completionItems && completionItems.length) 
        {
            var htmlStuff = "";
        
            // LOOP OVER EACH ITEM IN THE LIST
            for (var i = 0; i < completionItems.length; i++) 
            {
                var id = completionItems[i][0];
                var title = completionItems[i][1];
                var descr = completionItems[i][2];
                var link = completionItems[i][3];
            
            
                htmlStuff += "<div>";
                htmlStuff += "<input type=\"radio\" name=\"selectTextBoxItem\" value=\"" + id + "\" onclick=\"AjaxControlToolkit.SelectTextBoxBehavior.prototype._itemClick('" + this.HiddenFieldID + "', '" + this.ItemSelectedClientHandler + "','" + title.replace("'", "\\'") + "','" + this.get_element().id + "','" + this._completionListElementID + "','" + this._autoHide + "'); \" /> ";
                htmlStuff += "<a href=\"" + link + "\" target=\"_blank\">" + title + "</a> ";
                htmlStuff += "<span>(" + descr + ") </span>";
                htmlStuff += "</div>";
            
            }
            if(completionItems.length == this._completionSetCount)
                htmlStuff += "<div style=\"margin:5px; text-align:center;\">Showing top " + completionItems.length + " results</div>";
            else
                htmlStuff += "<div style=\"margin:5px; text-align:center;\">Showing " + completionItems.length + " results</div>";
            
            this._completionListElement.innerHTML = htmlStuff;
            var elementBounds = CommonToolkitScripts.getBounds(this.get_element());
            if(this.ListWidth)
                this._completionListElement.style.width = this.ListWidth;
            else
                this._completionListElement.style.width = Math.max(1, elementBounds.width - 2) + 'px';
                
            this._showCompletionList();
        }
        else 
        {
            // NO ITEMS EXIST
            this._setValue("");
            this._hideCompletionList();
        }
    }    
}

AjaxControlToolkit.SelectTextBoxBehavior.descriptor = {
    properties: [   {name: 'completionInterval', type: Number},
                    {name: 'completionList', isDomElement: true},
                    {name: 'completionListElementID', type: String},
                    {name: 'itemSelectedClientHandler', type: String},
                    {name: 'completionSetCount', type: Number},
                    {name: 'minimumPrefixLength', type: Number},
                    {name: 'serviceMethod', type: String},
                    {name: 'servicePath', type: String},
                    {name: 'enableCaching', type: Boolean},
                    {name: 'HiddenFieldID', type: String} ]
}

AjaxControlToolkit.SelectTextBoxBehavior.registerClass('AjaxControlToolkit.SelectTextBoxBehavior', AjaxControlToolkit.BehaviorBase);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();