﻿/************************************************************************************************/
/* Script Control                                                                               */
/************************************************************************************************/

Type.registerNamespace('Controls');

Controls.ScriptControl = function(element)
{
    Controls.ScriptControl.initializeBase(this, [element]);
    this._clientStateFieldId = null;
    this._clientState = null;
    this._clientStateEnabled = null;
}

Controls.ScriptControl.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.ScriptControl.callBaseMethod(this, 'initialize');         
        this.LoadClientStateField();
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {        
        this.SaveClientStateField();
        Controls.ScriptControl.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Get or Set Client State Field Id.
    /// </summary>
    get_ClientStateFieldId : function() {
        return this._clientStateFieldId;
    }, 
    
    set_ClientStateFieldId : function (value) { 
        this._clientStateFieldId = value;
    },
    
    /// <summary>
    /// Get or Set Client State Enabled
    /// </summary>
    get_ClientStateEnabled : function() {
        return this._clientStateEnabled;
    }, 
    
    set_ClientStateEnabled : function (value) { 
        this._clientStateEnabled = value;
    },
    
    /// <summary>
    /// Get or Set Client State Field Id.
    /// </summary>
    get_ClientState : function() {
        if(this._clientState == null)
        {
            this._clientState = new Object();
        }
        return this._clientState;
    }, 
    
    /// <summary>
    /// load client state from hidden field.
    /// </summary>
    LoadClientStateField : function() { 
        if(this._clientStateEnabled)
        {
            var e = $get(this._clientStateFieldId);
            if(e != null)
            {
              this._clientState = Sys.Serialization.JavaScriptSerializer.deserialize(e.value);
              this.LoadClientState();
            }
        }
    },
    
    /// <summary>
    /// Save client state to hidden field.
    /// </summary>    
    SaveClientStateField : function() { 
        if(this._clientStateEnabled)
        {
            this.SaveClientState();
            var e = $get(this._clientStateFieldId);
            if(e != null)
            {
              e.value = Sys.Serialization.JavaScriptSerializer.serialize(this.get_ClientState());
            }
        }
    }, 
    
    /// <summary>
    /// Load client state.
    /// </summary>
    LoadClientState : function() { 
    },

    /// <summary>
    /// Save client state.
    /// </summary>    
    SaveClientState : function() { 
    }
}
Controls.ScriptControl.registerClass('Controls.ScriptControl', Sys.UI.Control);

/************************************************************************************************/
/* Tab Control Behavior                                                                         */
/************************************************************************************************/

Controls.TabControlBehavior = function(element)
{
    Controls.TabControlBehavior.initializeBase(this, [element]);
    
    this._tabs = new Array();
    this._uniqueId = null;
    this._tabSelectedCssClass = "msfg-tabcontrol-tab-selected";
    this._tabPageSelectedCssClass = "msfg-tabcontrol-tabpage-selected";
    this._tabPageNotSelectedCssClass = "msfg-tabcontrol-tabpage-notselected";
    this._tabClientSideHandler = null;
    this._tabServerSideHandler = null;
}

Controls.TabControlBehavior.prototype = {

    /// <summary>
    /// Initialise Function
    /// </summary>
    initialize : function() {
        Controls.TabControlBehavior.callBaseMethod(this, 'initialize');            
        
        for(var i = 0; i < this._tabs.length; i++)
        {
                var tabInfo = this._tabs[i];
                var tabId = tabInfo.TabId;
                var tab = $get(tabId);
                var navigateUrlSet = tabInfo.NavigateUrlSet;
                var autoPostBack = tabInfo.AutoPostBack;
                
                if(!navigateUrlSet)
                {
                    if(autoPostBack)
                    {
                        this._tabServerSideHandler = Function.createDelegate(this, this._tabServerSideClick);
                        $addHandler(tab, "click", this._tabServerSideHandler)      
                    }
                    else
                    {
                        this._tabClientSideHandler = Function.createDelegate(this, this._tabClientSideClick);
                        $addHandler(tab, "click", this._tabClientSideHandler)       
                    }
                }
        }
    },
    
    /// <summary>
    /// Dispose Function
    /// </summary>
    dispose : function() {
        Controls.TabControlBehavior.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Handels a normal client side click to
    /// hide and show tab pages.
    /// </summary>
    _tabClientSideClick : function(e) { 
        for(var i = 0; i < this._tabs.length; i++)
        {
            var tabInfo = this._tabs[i];
            var tabId = tabInfo.TabId;
            var selected = false;
            
            if((e.target.parentNode.id == tabId) || (e.target.id == tabId))
            {
                this.set_SelectedIndex(i);
                var selected = true;
            }

            this._setTabSelected(tabInfo, selected);
        }
    },
    
    /// <summary>
    /// Handels a client side click that needs
    /// to perform a postback.
    /// </summary>
    _tabServerSideClick : function(e) {
        var tabIndex; 
        
        for(var i = 0; i < this._tabs.length; i++)
        {
            var tabInfo = this._tabs[i];
            var tabId = tabInfo.TabId;
            
            if(e.target.parentNode.id == tabId)
            {
                tabIndex = tabInfo.TabIndex;
            }
        }
        __doPostBack(this._uniqueId,tabIndex);
    },
    
    /// <summary>
    /// Set a tab and it's tab page to selected.
    /// </summary>
    _setTabSelected : function(tabInfo, selected) {
        var tabId = tabInfo.TabId;
        var tab = $get(tabInfo.TabId);
        var tabPage = $get(tabInfo.TabPageId);
        var navigateUrlSet = tabInfo.NavigateUrlSet;
        var autoPostBack = tabInfo.AutoPostBack;

        if(selected)
        {    
            Sys.UI.DomElement.addCssClass(tab, this._tabSelectedCssClass);
            
            if((tabInfo.CssClassKeyword != null) && (tabInfo.CssClassKeyword.length > 1))
            {   
                var PreferredTabSelectedCssClass = this._tabSelectedCssClass + tabInfo.CssClassKeyword;
                Sys.UI.DomElement.addCssClass(tab, PreferredTabSelectedCssClass);
            }
            
            Sys.UI.DomElement.removeCssClass(tabPage, this._tabPageNotSelectedCssClass);
            Sys.UI.DomElement.addCssClass(tabPage, this._tabPageSelectedCssClass);
        }
        else
        {
            Sys.UI.DomElement.removeCssClass(tab, this._tabSelectedCssClass);
            
            if((tabInfo.CssClassKeyword != null) && (tabInfo.CssClassKeyword.length > 1))
            {
                var PreferredTabNotSelectedCssClass = this._tabSelectedCssClass + tabInfo.CssClassKeyword;
                Sys.UI.DomElement.removeCssClass(tab, PreferredTabNotSelectedCssClass);
            }

            if(!navigateUrlSet)
            {
                Sys.UI.DomElement.removeCssClass(tabPage, this._tabPageSelectedCssClass);
                Sys.UI.DomElement.addCssClass(tabPage, this._tabPageNotSelectedCssClass);
            }
        }
    },
     
    /// <summary>
    /// Tab Collection.
    /// </summary>
    get_Tabs : function() { 
        return this._tabs; 
    },
    
    set_Tabs : function(value) { 
        this._tabs = value; 
    },
    
    /// <summary>
    /// Tab Collection.
    /// </summary>
    get_SelectedIndex : function() { 
        return this.get_ClientState().SelectedIndex;
    },
    
    set_SelectedIndex : function(value) { 
        this.get_ClientState().SelectedIndex = value;
        this.SaveClientStateField();
    },
    
    /// <summary>
    /// Tab Collection.
    /// </summary>
    get_UniqueId : function() { 
        return this._uniqueId; 
    },
    
    set_UniqueId : function(value) { 
        this._uniqueId = value; 
    },
        
    /// <summary>
    /// Tab Selected Css Class.
    /// </summary>
    get_TabSelectedCssClass : function() { 
        return this._tabSelectedCssClass;
    },
    
    set_TabSelectedCssClass : function(value) { 
        this._tabSelectedCssClass = value;
    },
    
    /// <summary>
    /// Tab Page Selected Css Class.
    /// </summary>
    get_TabPageSelectedCssClass : function() { 
        return this._tabPageSelectedCssClass; 
    },
    
    set_TabPageSelectedCssClass : function(value) { 
        this._tabPageSelectedCssClass = value;
    },
    
    /// <summary>
    /// Tab Page Not Selected Css Class.
    /// </summary>
    get_TabPageNotSelectedCssClass : function() { 
        return this._tabPageNotSelectedCssClass;
    },
    
    set_TabPageNotSelectedCssClass : function(value) { 
        this._tabPageNotSelectedCssClass = value;
    }
}

Controls.TabControlBehavior.registerClass('Controls.TabControlBehavior', Controls.ScriptControl);

/************************************************************************************************/
/* Cascading Drop Down List Behavior                                                            */
/************************************************************************************************/

Controls.CascadingDropDownListBehavior = function(element)
{
    Controls.CascadingDropDownListBehavior.initializeBase(this, [element]);
    
    // Properties.
    this._parentControlId = null;
    this._defaultText = null;
    this._category = null;
    this._loadingText = null;
    this._servicePath = null;
    this._serviceMethod = null;
    this._clientOnBeginRequest = null;
    this._clientOnRequestComplete = null;
    this._parentValue = null;
    this._keyDownTimer;
    this._webServiceTimer;
    
    // Variables.
    this._parentElement = null;
    this._changeHandler = null;
    this._parentChangeHandler = null;
    this._lastParentValues = null;
    this._selectedValue = null;
}

Controls.CascadingDropDownListBehavior.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.CascadingDropDownListBehavior.callBaseMethod(this, 'initialize');         
        
        // Get element
        var e = this.get_element();

        // Clear any items that may have been put there for server side convenience
        this._clearItems();

        e.CascadingDropDownCategory = this._category;        

        // Attach change handler to self
        this._changeHandler = Function.createDelegate(this, this._onChange);
        $addHandler(e, "change",this._changeHandler);
                
        if (this._parentControlId) {
            // Set properties on element so that children controls (if any) can have easy access
            this._parentElement = $get(this._parentControlId);
                    
            // Attach change handler to parent
            Sys.Debug.assert(this._parentElement != null, "Failed to find parent element '" + this._parentControlId + "'");
            if (this._parentElement) {
                e.CascadingDropDownParentControlID = this._parentControlId;
                this._parentChangeHandler = Function.createDelegate(this, this.onParentChangeWithDelay);
                
                $addHandler(this._parentElement, "change", this._parentChangeHandler);
                if (!this._parentElement.childDropDown) {
                    this._parentElement.childDropDown = new Array();
                }
                this._parentElement.childDropDown.push(this);
            }
            
            // Just show the default text
            this._showDefaultText();
            e.disabled = true;
        }
        else
        {
            // Simulate parent change to populate self, even if no parent exists.
            this.onParentChangeWithDelay(null, true);
        }
    },   
    
    /// <summary>
    /// Triggers and update with a custome value
    /// </summary>
    Update : function(value){
        this.set_ParentValue(value);
        this.onParentChangeWithDelay (null, true);
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {       
        var e = this.get_element();

        // Detach change handler for self
        if (this._changeHandler) {            
            $removeHandler(e, "change", this._changeHandler);
            this._changeHandler = null;
        }

        // Detach change handler for parent
        if (this._parentChangeHandler) {
            if (this._parentElement) {                
                $removeHandler(this._parentElement, "change", this._parentChangeHandler);
            }
            this._parentChangeHandler = null;
        } 
        
        Controls.CascadingDropDownListBehavior.callBaseMethod(this, 'dispose');
    },
    
    _isPopulated : function() {
        /// <summary>
        /// Determine whether the drop down has any items
        /// </summary>
        /// <returns type="Boolean">
        /// Whether the drop down has any items
        /// </returns>
        
        var items = this.get_element().options.length;
      
        if (this._promptText) {
            return items > 1;
        } else {
            return items > 0;
        }           
    },
    
    /// <summary>
    /// Clear the items from the drop down list
    /// </summary>
    /// <returns />
    _clearItems : function() {
        var e = this.get_element();
        while (0 < e.options.length) {
            e.remove(0);
        }
    },
    
    /// <summary>
    /// Show the loading text when web service is being contacted.
    /// </summary>
    /// <returns />
    _showLoadingText : function() {
        
        var e = this.get_element();
        
        // Populate prompt text (if available)
        if (this._loadingText) {
            var optionElement = new Option(this._loadingText, "");
            e.options[e.options.length] = optionElement;
        }
    },
    
    /// <summary>
    /// Show the default text.
    /// </summary>
    /// <returns />
    _showDefaultText : function() {
        
        var e = this.get_element();
        
        // Populate prompt text (if available)
        if (this._defaultText) {
            var optionElement = new Option(this._defaultText, "");
            e.options[e.options.length] = optionElement;
        }
    },    
    
    /// <summary>
    /// Set the contents of the DropDownList to the specified list
    /// </summary>
    /// <param name="list" mayBeNull="true" elementType="Object">
    /// Array of options (where each option has name and value properties)
    /// </param>
    /// <param name="inInit" type="Boolean" optional="true">
    /// Whether this is being called from the initialize method
    /// </param>
    /// <returns />
    _setOptions : function(list, inInit) {

        if (!this.get_isInitialized()) {
            return;
        }
        
        var e = this.get_element();
        
        // Remove existing contents
        this._clearItems();

        // Populate prompt text (if available)
        this._showDefaultText();

        // Add each item to the DropDownList, selecting the previously selected item
        var matchedSelectedValue = false;
        var defaultIndex = -1;

        if (list) {
            for (i = 0 ; i < list.length ; i++) {
                var listItemName = list[i].Name;
                var listItemValue = list[i].Value;
                
                if (list[i].isDefaultValue) {
                    defaultIndex = i;
                    if (this._defaultText) {
                        // bump the index if there's a prompt item in the list.
                        defaultIndex++;
                    }
                }

                var isSelectedValue = (listItemValue == this.get_SelectedValue());
                var optionElement = new Option(listItemName, listItemValue, false, isSelectedValue);
                matchedSelectedValue |= isSelectedValue;

                e.options[e.options.length] = optionElement;
            }
        }
        
        // If there is only one item in the list or there is 
        // one item and the default text make sure that is
        // selected.
        if(e.options.length == 2 && this._defaultText) {
            e.selectedIndex = 1;
            matchedSelectedValue = true;
        } else if (e.options.length == 1) {
            e.selectedIndex = 0;
            matchedSelectedValue = true;
        }
        
        if (matchedSelectedValue) {
            // The selected value from the client state has matched an
            // item in the list so select that item. This makes sure that
            // on postback we reselect items that we previous selected.
            this.set_SelectedValue(e.options[e.selectedIndex].value, e.options[e.selectedIndex].text);
        } else if (!matchedSelectedValue && defaultIndex != -1) {
            e.options[defaultIndex].selected = true;
            this.set_SelectedValue(e.options[defaultIndex].value, e.options[defaultIndex].text);
        } else if (!inInit && !matchedSelectedValue) {
            this.set_SelectedValue('', '');
        }

        // Call the parent changed method on the children
        if (e.childDropDown) {
            for(i = 0; i < e.childDropDown.length; i++) {
                e.childDropDown[i].onParentChangeWithDelay(null, inInit);
            }
        }
        
        if (this._defaultText) {
            e.disabled = !list || (0 == list.length);
        }
        
        // Call the request complete event.
        if(this._clientOnRequestComplete)
        {
            eval(this._clientOnRequestComplete);
        }
    },
    
    /// <summary>
    /// Handler for the drop down's change event
    /// </summary>
    /// <returns />
    _onChange : function() {
        if (!this._isPopulated()) {
            return;
        }

        var e = this.get_element();

        // Record the selected value in the client state
        if ((-1 != e.selectedIndex) && !(this._defaultText && (0 == e.selectedIndex))) {
            // if the selected item is not the deafult item or an the -1 unselected
            // value then store them.
            this.set_SelectedValue(e.options[e.selectedIndex].value, e.options[e.selectedIndex].text);
        } else {
            // if default or unselected then set the values to blank.
            this.set_SelectedValue('', '');
        }
    },
        
    /// <summary>
    /// When the parent selected value changes this is called, setting the Timeout function.
    /// Timeout value is set to 200 millisecs
    /// </summary>
    /// <returns />

    onParentChangeWithDelay : function(evt, inInit)
    {
        //if IE (as IE is the browser affected by the Error 500 problem)
        if(window.attachEvent)
        {    
            var scope = this;

            clearTimeout(this._keyDownTimer);
            this._keyDownTimer = setTimeout(function() {scope._onParentChange(evt, inInit);}, 200);   
        }
        else
        {
            //clear timeout and set timeout
            this._onParentChange(evt, inInit);
        }     
    }, 

    /// <summary>
    /// When the parent selected value changes this is called.
    /// </summary>
    /// <returns />
    _onParentChange : function(evt, inInit) {    
        
        if(typeof inInit == 'undefined') {
            inInit = false;
        }
    
        // Call the begin request event 
        if(this._clientOnBeginRequest)
        {
            eval(this._clientOnBeginRequest);
        }
        
        var e = this.get_element();
        
        // Create the known category/value pairs string for sending to the helper web service
        // Follow parent pointers so that the complete state can be sent
        // Format: 'name1:value1;name2:value2;...'
        var knownCategoryValues = '';
        var parentControlId = this._parentControlId;
        
        while (parentControlId) {
            var parentElement = $get(parentControlId);
            if (parentElement && (-1 != parentElement.selectedIndex)){
                var selectedValue = parentElement.options[parentElement.selectedIndex].value;
                
                if (selectedValue && selectedValue != "") {
                    knownCategoryValues = parentElement.CascadingDropDownCategory + ':' + selectedValue + ';' + knownCategoryValues;
                    parentControlId = parentElement.CascadingDropDownParentControlID;
                    continue;
                }
            } 
            break;
        }
        
        if (knownCategoryValues != '' && this._lastParentValues == knownCategoryValues) {
            return;
        }
         
        this._lastParentValues = knownCategoryValues;
          
        // we have a parent but it doesn't have a valid value
        if (knownCategoryValues == '' && this._parentControlId) {
            this._setOptions(null, inInit);
            return;
        }
    
        this._clearItems();
        this._showLoadingText();
        
        if(!inInit){
            this.set_SelectedValue('', '');
        }
        
        if (this._servicePath && this._serviceMethod) 
        {
            if (this._parentValue != null)
            {
                knownCategoryValues = 'parent' + ':' + this._parentValue;
            }
            
            //if IE (as IE is the browser affected by the Error 500 problem)
            if(window.attachEvent)
            {
                var scope = this;

                if(typeof(parentElement) != 'undefined')
                {
                    //If knownCategoryValues contains the cascadingdropdowncategory, call the webservice.
                    if(knownCategoryValues.match(parentElement.CascadingDropDownCategory))
                    {
                        //clear timeout and set timeout
                        clearTimeout(this._webServiceTimer);
                        this._webServiceTimer = setTimeout(function() {scope._getWebService(knownCategoryValues, inInit);}, 100);   
                    }
                    
                }
                else
                {
                    //clear timeout and set timeout
                    clearTimeout(this._webServiceTimer);
                    this._webServiceTimer = setTimeout(function() {scope._getWebService(knownCategoryValues, inInit);}, 100);   
                }

            }
            //other browsers
            else
            {
                this._getWebService(knownCategoryValues, inInit);
            }     
            
        }
    },      
    
    /// <summary>
    /// Call the web service
    /// </summary>
    /// <returns />
    _getWebService : function(knownCategoryValues, inInit)
    {
        // Call the helper web service 
        Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false,
        { knownCategoryValues:knownCategoryValues, category:this._category },
        Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError),inInit);        
    },
    
    /// <summary>
    /// Called when the web service compeltes it's call
    /// </summary>
    /// <returns />
    _onMethodComplete : function(result, inInit, methodName) {
        // Success, update the DropDownList
        this._setOptions(result, inInit);
    },

    /// <summary>
    /// Called when the web service fails its call
    /// </summary>
    /// <returns />
    _onMethodError : function(webServiceError, userContext, methodName) {
        // Indicate failure
        if (webServiceError.get_timedOut()) 
        {
            this._setOptions( [ this._makeNameValueObject('[Method timeout]') ] );
        } else 
        {
            //if not error 500 call _setOptions and pass in the error code.
            if(webServiceError.get_statusCode() != 500)
            {
                this._setOptions( [ this._makeNameValueObject('[Method error ' + webServiceError.get_statusCode() + ']') ] );
            }
            else
            {
                //pass default values if error 500.
                //This is done so that the user does not see the error code on the CDD box.
                this._setOptions(null, true);
            }
        }
    },
    
    /// <summary>
    /// Create an object with name and value properties set to the provided message
    /// </summary>
    /// <param name="message" type="String">
    /// Message
    /// </param>
    /// <returns type="Object">
    /// Object with name and value properties set to the message
    /// </returns>
    _makeNameValueObject : function(message) {
        return { 'Name': message, 'Value': message };
    },

    /// <value type="String">
    /// Selected value of the drop down
    /// </value>
    get_SelectedValue : function() {
        return this.get_ClientState().SelectedValue;
    },
    set_SelectedValue : function(value, text) {
        this.get_ClientState().SelectedValue = value;
        this.SaveClientStateField();
    },    
    
    /// <summary>
    /// Id of the parent control
    /// </summary>
    get_ParentControlId : function() { 
        return this._parentControlId;
    },
    set_ParentControlId : function(value) { 
        this._parentControlId = value;
    },
    
    /// <summary>
    /// default text.
    /// </summary>
    get_DefaultText : function() { 
        this._defaultText;
    },
    set_DefaultText : function(value) { 
        this._defaultText = value;
    },
    
    /// <value type="String">
    /// Loading text to to be displayed when getting the drop down's values from the web service
    /// </value>
    get_LoadingText : function() {
        return this._loadingText;
    },
    set_LoadingText : function(value) {
        this._loadingText = value;
    },
    
    /// <value type="String">
    /// Category of this drop down
    /// </value>    
    get_Category : function() {
        return this._category;
    },
    set_Category : function(value) {
        this._category = value;
    },
    
    /// <value type="String" mayBeNull="true">
    /// Path to the web service
    /// </value>
    get_ServicePath : function() {
        return this._servicePath;
    },
    set_ServicePath : function(value) {
            this._servicePath = value;
    },

    /// <value type="String">
    /// Name of the method to invoke on the web service
    /// </value>
    get_ServiceMethod : function() {
        return this._serviceMethod;
    },
    set_ServiceMethod : function(value) {
        this._serviceMethod = value;
    },
    
    /// <value type="String">
    /// Client On Begin Request Event Handler
    /// </value>
    get_ClientOnBeginRequest : function() {
        return this._clientOnBeginRequest;
    },
    set_ClientOnBeginRequest : function(value) {
        this._clientOnBeginRequest = value;
    },
    
    /// <value type="String">
    /// Client On Complete Request Event Handler
    /// </value>
    get_ClientOnRequestComplete : function() {
        return this._clientOnRequestComplete;
    },
    set_ClientOnRequestComplete : function(value) {
        this._clientOnRequestComplete = value;
    },

    /// <value type="String">
    /// Client On Complete Request Event Handler
    /// </value>
    get_ParentValue : function() {
        return this._parentValue;
    },
    set_ParentValue : function(value) {
        this._parentValue = value;
    }

}

Controls.CascadingDropDownListBehavior.registerClass('Controls.CascadingDropDownListBehavior', Controls.ScriptControl);

/************************************************************************************************/
/* AutoSuggestTextBox Behavior                                                                  */
/************************************************************************************************/

Controls.AutoSuggestTextBoxBehavior = function(element)
{
    Controls.AutoSuggestTextBoxBehavior.initializeBase(this, [element]);
    
    // Properties.
    this._servicePath = '';
    this._serviceMethod = '';
    this._selectedValue = '';
    this._selectedText = '';
    this._suggestionClassName = '';
    this._containerClassName = '';
    this._suggestionTitleText = 'Please select:';
    this._maxRecords = 20;
    this._maxDisplayed = 10;
    this._freeCharacters = 1;
    this._isCaseSensitive = true;
    this._matchAnywhere = false;
    this._showNoMatch = true;
    this._matchTextWidth = true;
    this._cacheSearch = true;
    this._format = 1;           // enum (1.List, 2.Table)
    this._popupPosition = 2;    // enum (1.Above,2.Below)
    this._noMatchMessage = '';
    this._selectionColour = null;
    this._selectionTextColour = null;
    this._containerId = '';
    this._selectedItemDisplayTemplate = '#{1}';
    this._searchOnFocus = false;
    this._maxHeightBeforeScrolling = null;
    this._displayInnerContainer = false;
    
    // Event Listeners
    this._onBeginSearchClientFunction = null;
    this._onCustomValueEnteredClientFunction = null;
    this._onSearchCompleteClientFunction = null;
    this._onSelectedItemChangedClientFunction = null;
    
    // Event handlers
    this._onkeydownHandler = null;
    this._onkeyupHandler = null;
    this._onkeypressHandler = null;
    this._onblurHandler = null;
    this._onfocusHandler = null;
    this._onSuggestionsDivMouseOutHandler = null;
    this._onSuggestionsDivMouseOverHandler = null;
    this._onSuggestionsDivFocusHandler = null;
    this._onSuggestionsDivFocusOutHandler = null;
    
    // Associated controls and elements.
    this._hiddenInput = null;
    this._suggestionsDiv = null;
    this._containerDiv = null;
    this._ieframe = null;
    
    // Internal data and state variables
    this._suggestions = [];
    this._additionalParameters = {};
    this._selectedIndex = -1;
    this._suggestionIndex = -1;
    this._handlingRequest = false;
    this._pendingRequest = false;
    this._lastRequestString = '';
    this._newSearch = true;
    this._suggestionsVisible = false;
    this._isSuggestionsDivFocused = false;
    this._scrollingEnabled = false;
 }

Controls.AutoSuggestTextBoxBehavior.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.AutoSuggestTextBoxBehavior.callBaseMethod(this, 'initialize');         
        // Get dom element
        var e = this.get_element();
        // control specific initialization
        var browser = navigator.userAgent.toLowerCase();
        var hidden_id = e.id + '_hidden';
        //e.value = this.get_SelectedText();
        e.IE = (browser.indexOf('msie') != -1);
		e.IsOpera = (browser.indexOf('opera') != -1);
		
		if (this._maxHeightBeforeScrolling != null && this._maxHeightBeforeScrolling > 0) {
		    this._scrollingEnabled = true;
		}
		
		// add event handlers..
        this._keyupHandler = Function.createDelegate(this, this.onKeyUp);
        $addHandler(e, "keyup", this._keyupHandler);
         this._keydownHandler = Function.createDelegate(this, this.onKeyDown);
        $addHandler(e, "keydown", this._keydownHandler);
        this._onblurHandler = Function.createDelegate(this, this.onBlur);
        $addHandler(e, "blur", this._onblurHandler);
       
        // does control focus initiate a search?
        if(this._searchOnFocus) {
            this._onfocusHandler = Function.createDelegate(this, this.onFocus);
            $addHandler(e, "focus", this._onfocusHandler);
        }

        if (e.IsOpera) {
            $addHandler(e, "keypress", this._keyupHandler);
        }
		
		// insert related form fields (prototype style..)
		new Insertion.After(e, '<input type="hidden" name="'+hidden_id+'" id="'+hidden_id+'"/>');
		this._hiddenInput = $(hidden_id);
		
		// set up the suggestions div
		this._createSuggestionsDiv();

		if(e.IE) {
			// IE evil..
			this._createIEFrame();
			
			if (this._scrollingEnabled == true) {
			    // Hookup suggestions div event handlers for IE.         
                this._onSuggestionsDivFocusOutHandler = Function.createDelegate(this, this.onSuggestionsDivFocusOut);
                $addHandler(this._containerDiv, "focusout", this._onSuggestionsDivFocusOutHandler);
            }
		}
		
		if (this._scrollingEnabled == true) {
		    this._onSuggestionsDivMouseOutHandler = Function.createDelegate(this, this.onSuggestionsDivMouseOut);
            $addHandler(this._containerDiv, "mouseout", this._onSuggestionsDivMouseOutHandler);
            
            this._onSuggestionsDivMouseOverHandler = Function.createDelegate(this, this.onSuggestionsDivMouseOver);
            $addHandler(this._containerDiv, "mouseover", this._onSuggestionsDivMouseOverHandler);
    		
		    this._onSuggestionsDivFocusHandler = Function.createDelegate(this, this.onSuggestionsDivFocus);
            $addHandler(this._containerDiv, "focus", this._onSuggestionsDivFocusHandler);
        }
        
		Controls.AutoSuggestTextBoxBehavior.registerInstance(this);
		window.onresize = Controls.AutoSuggestTextBoxBehavior.repositionSuggestionDivs;
    },
    
    /// <summary>
    /// Suggestions div mouseover event handler.
    /// </summary>
    onSuggestionsDivMouseOver : function() {
        this._isSuggestionsDivFocused = true;
    },
    
    /// <summary>
    /// Suggestions div mouseout event handler.
    /// </summary>
    onSuggestionsDivMouseOut : function() {
        this._isSuggestionsDivFocused = false;
    },
    
    /// <summary>
    /// Suggestions div focus event handler.
    /// </summary>
    onSuggestionsDivFocus : function() {
        this._isSuggestionsDivFocused = true;
    },
    
    onSuggestionsDivFocusOut : function() {
        this._isSuggestionsDivFocused = false;
        this.onBlur();
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {       
        var e = this.get_element();
        Controls.AutoSuggestTextBoxBehavior.callBaseMethod(this, 'dispose');
         // Detach change handler for self
        if (this._keyupHandler) {            
            $removeHandler(e, "keyup", this._keyupHandler);
            this._keyupHandler = null;
        }
        if (this._keydownHandler) {            
            $removeHandler(e, "keydown", this._keydownHandler);
            this._keydownHandler = null;
        }
        if (this._onblurHandler) {            
            $removeHandler(e, "blur", this._onblurHandler);
            this._onblurHandler = null;
        }
        if (this._onfocusHandler) {
            $removeHandler(e, "focus", this._onfocusHandler);
            this._onfocusHandler = null;
        }
        
        if (this._onSuggestionsDivFocusHandler) {
            $removeHandler(this._containerDiv, "focus", this._onSuggestionsDivFocusHandler);
            this._onSuggestionsDivFocusHandler = null;
        }
        
        if (this._onSuggestionsDivMouseOutHandler) {
            $removeHandler(this._containerDiv, "mouseout", this._onSuggestionsDivMouseOutHandler);
            this._onSuggestionsDivMouseOutHandler = null;
        }
        
        if (this._onSuggestionsDivMouseOverHandler) {
            $removeHandler(this._containerDiv, "mouseover", this._onSuggestionsDivMouseOverHandler);
            this._onSuggestionsDivMouseOverHandler = null;
        }
        
        if (e.IE) {
            if (this._onSuggestionsDivFocusOutHandler) {
                $removeHandler(this._containerDiv, "focusout", this._onSuggestionsDivFocusOutHandler);
                this._onSuggestionsDivFocusOutHandler = null;
            }
        }
    },
    
    /// <summary>
    /// Create the popup suggestions container.
    /// </summary>
    _createSuggestionsDiv : function() {
        this._suggestionsDiv = document.createElement("div");
       
		// set configurable display attributes
		this._suggestionsDiv.className = this._suggestionClassName;

		var divStyle = this._suggestionsDiv.style;
		// set non-configurable behaviour attributes
		divStyle.cursor = 'default';
		divStyle.position = 'absolute';
		// make sure it appears above other elements
		divStyle.zIndex   = 10000;
		// initial state is hidden - no search has been performed yet
		divStyle.display  = "none";
		
		this.get_element().parentNode.appendChild(this._suggestionsDiv);

		if (this._displayInnerContainer)
		{
			// Display title above _containerDiv
		    var _suggestionsTitle = document.createElement("h3");
		    _suggestionsTitle.appendChild(document.createTextNode(this._suggestionTitleText));
		    this._suggestionsDiv.appendChild(_suggestionsTitle);
		    
		    this._containerDiv = document.createElement("div");
		    this._containerDiv.className = this._suggestionContainerClassName;
		    this._suggestionsDiv.appendChild(this._containerDiv);
		}
		
	    if (this._containerDiv == null)
	        this._containerDiv = this._suggestionsDiv;
    },
    
    // create the IFRAME that prevents windowed controls, such as 'select' and 'object'
	// from showing through the suggestion list in IE
	// see [http://weblogs.asp.net/bleroy/archive/2005/08/09/how-to-put-a-div-over-a-select-in-ie.aspx]
    _createIEFrame : function() {
        this._ieframe = document.createElement('iframe');
		var frameStyle = this._ieframe.style;
		this._ieframe.setAttribute("scrolling", "no");
		this._ieframe.setAttribute("frameborder", "0");
		this._ieframe.setAttribute("src", "javascript:'<html></html>'");   // its only ever in IE
		frameStyle.position = "absolute";
		frameStyle.border = "none";
		frameStyle.display = "none";
		frameStyle.zIndex = 0;
		this.get_element().parentNode.appendChild(this._ieframe);
    },
    
    // positions the suggestions div below the textbox and makes it visible.
	showSuggestions: function() {
		var divStyle = this._suggestionsDiv.style;
		// bail out if its already visible
        // make sure its correctly positioned
        this.positionSuggestionsDiv();
        // show it
        divStyle.display = '';
        // IE evil..
        if(this._ieframe != null) {
	        this._ieframe.style.display = '';
        }
        
        this.closeAllOtherSuggestionDivs(this._suggestionsDiv)
        this._suggestionsVisible = true;
	},
	
    /// <summary>
    /// Closes all currently open suggestion divs.
    /// </summary>
    closeAllOtherSuggestionDivs : function(element) {
        for(var index = 0; index < Controls.AutoSuggestTextBoxBehavior.registeredInstances.length; index++ ) {
            var asInstance = Controls.AutoSuggestTextBoxBehavior.registeredInstances[index];
            if (element != asInstance._suggestionsDiv) {
                asInstance.hideSuggestions();
            }
        }
    },
	
    /// Hides the suggestions popup.
    hideSuggestions : function() {
        this._suggestionsDiv.style.display = 'none';
		if(this._ieframe != null) {
			this._ieframe.style.display = 'none';
		}
		this._selectedIndex = -1;
		this._suggestionsVisible = false;
    },
    
    /// Highlights the next selection in the popup list.
    moveSelectionUp : function(evt) {    
        this.updateSelection(this._selectedIndex - 1, evt);
    },
    
    /// Highlights the next previous selection in the popup list.
    moveSelectionDown : function(evt) {
        if(this._selectedIndex == (this._suggestions.length - 1)) {
            this_selectedIndex = -1;
        }
        
        this.updateSelection(this._selectedIndex + 1, evt);
    },
        
    /// Processes the user's text input
    setInputFromSelection : function() {
        var e = this.get_element();

        if(this._suggestions.length > 0 && this._suggestionIndex > -1) {
			var suggestion  = this._suggestions[this._suggestionIndex];
			// set user selection (note that the underlying data may not
			// be a simple string - it could be pipe-delimited with
			// the value stored in the first column
			if(this.isTabular()) {
			    // use prototype templates
			    var obj = new Object();
			    var colValues = suggestion.Text.split('|');
			    // sample template '#{1}, {2}
			    var t = new Template(this._selectedItemDisplayTemplate);
			    for(var i = 1; i <= colValues.length; i++) {
			        obj[i.toString()] = colValues[i-1];
			    }
			    e.value = t.evaluate(obj);
			}
			else {
			    e.value = suggestion.Text;
			}
			this._lastRequestString = e.value;
			this.set_SelectedValue(suggestion.Value);
			this.hideSuggestions();
			// Call the request complete event.
            if(this._onSelectedItemChangedClientFunction)
            {
                var jsString = this._onSelectedItemChangedClientFunction + this.generateJSParameterList(e.value);
                eval(jsString);
            }
			
		}
		else {
			// Assume null value is empty string for now.
			this.set_SelectedValue("");
			if(this._onCustomValueEnteredClientFunction) {
                eval((this._onCustomValueEnteredClientFunction + this.generateJSParameterList(e.value)));
            }
		}
    },
    
    // creates a string of javascript based upon user text and underlying control value.
    // used in onSelectedItem and onUserValue callbacks
    generateJSParameterList : function(selectedText) {
        return ("('" + selectedText.gsub(/["']/, '') +"','" + this.get_SelectedValue() + "')");
    },
    
    handleTextInput : function(autoFocus) {
        var previousRequest = (this._lastRequestString ? this._lastRequestString : "");
        
        // lengths of the previous and current request strings
        var prLength = previousRequest.length;
        var lrLength = 0;
        // set the search string from the text field.
        this.set_LastRequestString();
        lrLength = this._lastRequestString.length;
		// is text less than free character threshold
		if(lrLength < this._freeCharacters) {
			if(this._suggestions.length > 0) {
				this._suggestions = [];
			}
			this._newSearch = true;
			if(this._suggestionsVisible) {
			    this.hideSuggestions();
			}
			return;
		}
		// decide whether or not to start new search
		if((this._cacheSearch == false) || prLength == 0 || (lrLength == 0)) {
		    this._newSearch = true;
		}
		else if(prLength == lrLength) {
		    this._newSearch = (previousRequest.startsWith(this._lastRequestString) == false);
		}
		else if(prLength > 0) {
   			// both previous and current have at least one character
   			// if there is a mismatch in the first letter, initiate a new search
   			if(this._lastRequestString.startsWith(previousRequest.slice(0, this._freeCharacters))== false) {
   				this._newSearch = true;
   			}
   		}
        
		// there is text. Is it a new search?
		if(this._newSearch) {

			// have we exceeded the number of characters allowed before autosearching begins?
			if(lrLength >= this._freeCharacters) {
				this.sendRequestForSuggestions();
			}
		}

		else {
			// resuse existing seach
			this.onSearchComplete(false);
		}
    },
    
    // resets control to initial state and clears any existing searches.
    reset: function() {
        this._selectedIndex = -1;
        this._suggestionIndex = -1;
        this._handlingRequest = false;
        this._pendingRequest = false;
        this._newSearch = true;
        this._lastRequestString = '';
        if(this._suggestions.length > 0) {
            this._suggestions = [];
            this._suggestionIndex = -1;
        }
        this.set_SelectedValue('');
        this.set_SelectedText('');
    },
    
    // updates the search list based upon user selection
  	updateSelection: function(n, evt) {
  		// assume, for now, that the span continer is the
  		// top level suggestions div. 
  		// this may change if it needs inner divs for styling purposes
  		var container = this._containerDiv;
  		var isTabular = this.isTabular();
  		var e;
  		var index = 0;
  		
  		if(isTabular) {
  			if (container.firstChild != null)
  			    // the table's tbody is the container and the rows are the child nodes.
  			    container = container.firstChild.firstChild;
  		}
		
		if(this._selectedIndex > -1) {
			e = container.childNodes[this._selectedIndex];
			if(e) {
				e.style.backgroundColor = "";
				e.style.color = "";
			}
		}
		if(n < 0) {
		    index = container.childNodes.length - 1;
		}
		else if(n < container.childNodes.length) {
		    index = n;
		}
       
        if (container.hasChildNodes())
        {
	        e = container.childNodes[index];
	    }

        if (e)
        {
            this._selectedIndex = index;           
            e.style.backgroundColor = this._selectionColour;
            e.style.color = this._selectionTextColour;
            this._suggestionIndex = parseInt(e.getAttribute("suggestionindex"));
            
            var borderWidth;
            var elementHeight
                    
            if (index > 0) 
            {
                borderWidth = e.style.borderWidth;
                elementHeight = e.offsetHeight;
            
                if (borderWidth > 0) 
                {
                   elementHeight += borderWidth;
                }
            }
            else
            {
                borderWidth = 0;
                elementHeight = 0;
            }


            var keyCode = null;
            evt = (evt) ? evt : ((window.event) ? event : null);

            if(evt != null) 
            { 
                if(index == 0) 
                {
                    this._containerDiv.scrollTop = 0;
                }
                else if (index == (container.childNodes.length - 1)) 
                {
                    this._containerDiv.scrollTop = this._containerDiv.scrollHeight;
                }
                else 
                {
                    keyCode = (evt.charCode) ? evt.charCode : ((evt.which) ? evt.which : evt.keyCode);
                
                    if(keyCode == 40) 
                    {
                        this._containerDiv.scrollTop += elementHeight;
                    }
                    else if(keyCode == 38) 
                    {
                        this._containerDiv.scrollTop -= elementHeight;
                    }
                }
	        }
	    }
  	},
    
    positionSuggestionsDiv : function() {
        var e = this.get_element();
        var textPos = this.toDocumentPosition(e);
        var scrollOffsets = this.getScrollOffsets();
		var divStyle = this._suggestionsDiv.style;
		var dimensions = this.getElementDimensions(this._suggestionsDiv);
		var x_adjust = 0;
		var y_adjust = 0;
		var textHeight = e.offsetHeight;
		if(this._containerId != '') {
		    var container = $(this._containerId);
		    if(container) {
		        var container_width = parseInt(container.offsetWidth);
		        var screenWidth = window.screen.availWidth - 18;
		        x_adjust += (screenWidth - container_width)/2;
		    }
		}
		// IE in the framework positions the suggestions div relative to
		// the page rather than the viewport. In other words, it does not
		// take into account the fact that the page may be scrolled.
		// so we must 
		//if(e.IE) {
		//   y_adjust = scrollOffsets.y;    
		//}
		// top and left
		if(this._popupPosition == 1) {
		    y_adjust = 4;   // cosmetic
		    // above
		    divStyle.top = ((textPos.y - dimensions.height) - y_adjust) + "px";
		}
		else {
		    divStyle.top  = ((textPos.y + textHeight) + y_adjust) + "px";
		}
		divStyle.left = (textPos.x - x_adjust) + "px";
		if (this._matchTextWidth) {
			divStyle.width = (e.offsetWidth - this._computePadding()) + "px";
		}
		
		if(e.IE) {
			// show the IFRAME that covers certain types of control
			// that would otherwise show through
			var frameStyle = this._ieframe.style;
			frameStyle.top = divStyle.top;
			frameStyle.left = divStyle.left;
			frameStyle.width = divStyle.width;
			frameStyle.height = dimensions.height + "px";
		}
    },
    
    // computes the width and height in pixels for a specified
    // element.
    getElementDimensions: function(element) {
         var display = $(element).style.display;
         if (display != 'none' && display != null) // Safari bug
           return {width: element.offsetWidth, height: element.offsetHeight};

         // All *Width and *Height properties give 0 on elements with display none,
         // so enable the element temporarily
         var els = element.style;
         var originalVisibility = els.visibility;
         var originalPosition = els.position;
         var originalDisplay = els.display;
         els.visibility = 'hidden';
         els.position = 'absolute';
         els.display = 'block';
         var originalWidth = element.clientWidth;
         var originalHeight = element.clientHeight;
         els.display = originalDisplay;
         els.position = originalPosition;
         els.visibility = originalVisibility;
         return {width: originalWidth, height: originalHeight};
	},
	 toDocumentPosition: function(element) {
      return this._toAbsolute(element);
   },

   /**
    *  Compute the elements position in terms of the window viewport
    *  so that it can be compared to the position of the mouse (dnd)
    *  This is additions of all the offsetTop,offsetLeft values up the
    *  offsetParent hierarchy, ...taking into account any scrollTop,
    *  scrollLeft values along the way...
    *
    * IE has a bug reporting a correct offsetLeft of elements within a
    * a relatively positioned parent!!!
    **/
   _toAbsolute: function(element) {

      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
         return this._toAbsoluteMozilla(element);

      var x = 0;
      var y = 0;
      var parentElement = element;
      while ( parentElement ) {

         var borderXOffset = 0;
         var borderYOffset = 0;
         if ( parentElement != element ) {
            var borderXOffset = parseInt(this.getElementsComputedStyle(parentElement, "borderLeftWidth" ));
            var borderYOffset = parseInt(this.getElementsComputedStyle(parentElement, "borderTopWidth" ));
            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
         }

         //Check if this element is the AS textbox if so don't include the scrollLeft/Top attributes
         //to avoid IE bug where the suggestion DIV shifts along with the above attributes.
         if(parentElement == element) {
            x += parentElement.offsetLeft + borderXOffset;
            y += parentElement.offsetTop + borderYOffset;         
         }
         else {
            x += parentElement.offsetLeft - parentElement.scrollLeft + borderXOffset;
            y += parentElement.offsetTop - parentElement.scrollTop + borderYOffset;
         }
         
         parentElement = parentElement.offsetParent;
      }

      return { x:x, y:y };
   },

   /**
    *  Mozilla did not report all of the parents up the hierarchy via the
    *  offsetParent property that IE did.  So for the calculation of the
    *  offsets we use the offsetParent property, but for the calculation of
    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
    *  property instead so as to get the scroll offsets...
    *
    **/
   _toAbsoluteMozilla: function(element) {
      var x = 0;
      var y = 0;
      var parentElement = element;
      while ( parentElement ) {
         x += parentElement.offsetLeft;
         y += parentElement.offsetTop;
         parentElement = parentElement.offsetParent;
      }

      parentElement = element;
      while ( parentElement &&
              parentElement != document.body &&
              parentElement != document.documentElement ) {
         if ( parentElement.scrollLeft  )
            x -= parentElement.scrollLeft;
         if ( parentElement.scrollTop )
            y -= parentElement.scrollTop;
         parentElement = parentElement.parentNode;
      }

      return { x:x, y:y };
   },
    
    // computes the offset due to any page scrolling.
    getScrollOffsets: function() {
        var scrollX = 0, scrollY = 0;
        if (document.body && typeof document.body.scrollTop != "undefined") {
			scrollX += document.body.scrollLeft;
			scrollY += document.body.scrollTop;
			if (document.body.parentNode && 
				typeof document.body.parentNode.scrollTop != "undefined") {
				scrollX += document.body.parentNode.scrollLeft;
				scrollY += document.body.parentNode.scrollTop;
			}
		} else if (typeof window.pageXOffset != "undefined") {
			scrollX += window.pageXOffset;
			scrollY += window.pageYOffset;
    	}
    	return {x:scrollX, y:scrollY};
    },
    
    // Sends the search to the server and sets the handlingRequest flag
    sendRequestForSuggestions: function() {
        
        if (this._handlingRequest) {
           this._pendingRequest = true;
           return;
        }
   
    	this._handlingRequest = true;
    	// notify any listerners?
    	if(this._onBeginSearchClientFunction) {
    	    eval((this._onBeginSearchClientFunction + "('" + this._lastRequestString.gsub(/["']/,'') + "')"));
    	}
    	this.callAjaxEngine();
    },
    
    // Called after the suggestions have been deserialized.
    onSearchComplete: function(isNewSearch) {
        // do we have any suggestsion?
		if (this._suggestions.length == 0) {
			//this.hideSuggestions();
			this.setError();
			this._showNoMatch ? this.showSuggestions() : this.hideSuggestions();
		}
		else {
			var numShown = this.updateSuggestionsDiv();
			this._newSearch = false;
			if(numShown > 0) {
				this.showSuggestions();
				this.updateSelection(0, null);
			}
			else {
				this._showNoMatch ? this.showSuggestions() : this.hideSuggestions();
			}
			
		}
		
		if(isNewSearch) {
		// reset the request handling flag - we can now process new requests
			this._handlingRequest = false;
			// check to see if a request is pending (queued). If there is then send it.
			if (this._pendingRequest) {
			    this._pendingRequest = false;
				this.set_LastRequestString();
				if(this._lastRequestString.length >= this._freeCharacters) {
				    this.sendRequestForSuggestions();
				}
			}
		}
	},
	
	// translates the value in the text field into a request search string.
	set_LastRequestString: function() {
	    var v = this.get_element().value;
	    var idx = v.length;
	    if(this.isTabular()) {
	        // ignore any other columns that are being displayed
	        idx = v.indexOf(',');
	        if(idx == -1) {
	            idx = v.length;
	        }
	    }
	    this._lastRequestString = v.slice(0, idx);
	},
	
	// clears any existing suggestions and inserts new ones
	// returns the number of suggestions created
	updateSuggestionsDiv: function() {
		// regex flags
		var regExpFlags = "";
		var startRegExp = "^";
		var numShown = 0;
		var reChars = /[\(\).\[\]*+]/;
		var e = this.get_element();
		
		this._containerDiv.innerHTML = "";
		this._containerDiv.style.height = "";

		// map control properties to regex search flags
		if (!this._isCaseSensitive) { regExpFlags = 'i'; }
		if (this._matchAnywhere) { startRegExp = ''; }
		// create the regex used to extract user search string from the
		// list of suggestions, so that it can be highlighted
		var re  = new RegExp(startRegExp + this._lastRequestString.gsub(reChars, function(match) {return "\\" + match[0];}), regExpFlags);
		// deal with tabular data
		if(this.isTabular()) {
			var tbl = document.createElement("table");
			var tbody = document.createElement("tbody");
			var tr;
			var rowIndex = 0;
			for(var i = 0; i < this._suggestions.length; i++) {
				if(this._suggestions[i].Text.match(re)) {
					tr = this.createSuggestionRow(this._suggestions[i].Text, rowIndex, re);
					tr.onmouseover   = this.mouseoverHandler.bindAsEventListener(this);
					tr.onclick       = this.itemClickHandler.bindAsEventListener(this);
					tr.setAttribute("id", ("r_" + rowIndex));
					tr.setAttribute("suggestionindex", i);
					tbody.appendChild(tr);
					rowIndex++;
					if(rowIndex >= this._maxDisplayed) {
					    break;
					}
				}
			}
			tbl.setAttribute("cellspacing", 0);
			tbl.appendChild(tbody);
			numShown = tbody.childNodes.length;
			this._containerDiv.appendChild(tbl);
		}
		else {
			// create an array of span elements
			var suggestLines = this.createSuggestionSpans(re);
			// add them to the parent div
			for (var i = 0 ; i < suggestLines.length ; i++) {
				this._containerDiv.appendChild(suggestLines[i]);
			}
			numShown = suggestLines.length;
		}
		if(numShown == 0) {
			this.setError();	
		}

		var dimensions = this.getElementDimensions(this._containerDiv);

        if (this._scrollingEnabled) {
		    if (dimensions.height > this._maxHeightBeforeScrolling) {
		        this._containerDiv.style.height = this._maxHeightBeforeScrolling + "px";
		        this._containerDiv.style.overflow = 'auto';
		    }
		}
		
		return numShown;
	},
	
	setError: function() {
		var pNode = document.createElement("p");
		pNode.appendChild(document.createTextNode(this._noMatchMessage));
		pNode.className = "nomatch";
		this._containerDiv.innerHTML = "";
		this._containerDiv.appendChild(pNode);
		this._selectedIndex = -1;
		this._suggestionIndex = -1;
        this._containerDiv.style.height = "";
	},
	
	// generates a table row from the pipe delimited string value
	createSuggestionRow: function(rowData, rowIndex, re) {
		var start = 0;
		var end = rowData.indexOf('|');
		var len = rowData.length;
		var trow = document.createElement("tr");
		var td;
		var col = 0;
		var rowValue = '';
		// scan the row data
		if(end == -1) {
			end = len;
		}
		while(start < len) {
			rowValue = rowData.slice(start, end);
			td = document.createElement("td");
			
			if(rowValue.match(re)) {
				var textValues = this.splitTextValues(rowValue, this._lastRequestString.length,re);
				var matchSpan = document.createElement("span");
				matchSpan.className = 'match';
				matchSpan.id = "m_" + rowIndex;
				matchSpan.appendChild(document.createTextNode(textValues.mid));
				
				if(textValues.start.length > 0) {
					td.appendChild(document.createTextNode(textValues.start));
				}
				td.appendChild(matchSpan);
				td.appendChild(document.createTextNode(textValues.end));
			}
			else {
				td.appendChild(document.createTextNode(rowValue));
			}
			td.className = 'col_' + col;
			td.setAttribute("id", (start + "_" + rowIndex));
			col++;
			
			trow.appendChild(td);
			start = end+1;
			end = rowData.indexOf('|', start);
			if(end == -1) {
				end = len;
			}
		}
		return trow;
	},
	
	// generates an array of <span> elements, one for each suggestion
	// returned by the server
	createSuggestionSpans: function(re) {
	    var control_id = this.get_element().id;
		var spanIndex = 0;
		// build the array of suggestion elements
		var suggestionSpans = [];
		for (var i = 0 ; i < this._suggestions.length ; i++) {
			var result = this._suggestions[i].Text.match(re);
			if(result) {
				suggestionSpans.push(this.createSuggestionSpan(control_id, i, spanIndex, re));
				spanIndex++;
			}
			if(spanIndex >= this._maxDisplayed) {
			    break;
			}
		}
		return suggestionSpans;
	},
	
	// Creates a span element based upon the specified string value in the
	// suggestions array and the user search criteria in the regExp parameter.
	// The object is to seperate the two so that the search and the result are clearly
	// visible (e.g. [A]ccountant.
	createSuggestionSpan: function(control_id, suggestionIndex, spanIndex, regExp) {
		var suggestion = this._suggestions[suggestionIndex];

		var suggestionSpan = document.createElement("span");
		suggestionSpan.className = 'suggestion';
		suggestionSpan.style.width   = '100%';
		suggestionSpan.style.display = 'block';
		suggestionSpan.id            = control_id + "_" + spanIndex;
		suggestionSpan.onmouseover   = this.mouseoverHandler.bindAsEventListener(this);
		suggestionSpan.onclick       = this.itemClickHandler.bindAsEventListener(this);
		// store the index to the suggestions array
		// remember, not all suggestions are displayed
		suggestionSpan.setAttribute("suggestionindex", suggestionIndex);

		var textValues = this.splitTextValues(suggestion.Text,
			this._lastRequestString.length,
			regExp);

		var textMatchSpan = document.createElement("span");
		textMatchSpan.className     = 'match';
		textMatchSpan.appendChild(document.createTextNode(textValues.mid));
		suggestionSpan.appendChild(document.createTextNode(textValues.start));
		suggestionSpan.appendChild(textMatchSpan);
		suggestionSpan.appendChild(document.createTextNode(textValues.end));

		return suggestionSpan;
	},
	
	// handles the mouseover handler in the suggestions list. Finds
	// the index of the span the mouse is over and highlights it
	// via a call to 'updateSelection'
	mouseoverHandler: function(evt) {
		var src = evt.srcElement ? evt.srcElement : evt.target;
		var index = parseInt(src.id.substring(src.id.lastIndexOf('_')+1));
		this.updateSelection(index, evt);
	},
	
	// Called when the user clicks a suggestion span.
	itemClickHandler: function(evt) {
	    this._isSuggestionsDivFocused = false
		this.mouseoverHandler(evt);
		this.setInputFromSelection();
		this.hideSuggestions();
	},
	
	// Finds the position of the users search string (e.g. 'Ac') within
	// the suggestion text (e.g. Turf [Ac]countant)
	splitTextValues: function(text, len, regExp) {
		var startPos  = text.search(regExp);
		var matchText = text.substring(startPos, startPos + len);
		var startText = startPos == 0 ? "" : text.substring(0, startPos);
		var endText   = text.substring(startPos + len);
		return { start: startText, mid: matchText, end: endText };
	},
    
    /// <summary>
    /// When the parent selected value changes this is called.
    /// </summary>
    /// <returns />
    callAjaxEngine : function() {
        var e = this.get_element();
        if (this._servicePath && this._serviceMethod) {
            var params = { 
                searchString:this._lastRequestString,
                maxRecords:this._maxRecords,
                matchAnywhere:this._matchAnywhere,
                isCaseSensitive:this._isCaseSensitive,
                additionalParameters:this._additionalParameters
            };
            // Call the helper web service
            Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false,
                params,
                Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError));
        }
    },
    
    // computes the amount of horizontal (left and right) padding in the suggestionsDiv
  	// taking into account any styling thats been applied to it.
    _computePadding : function() {
        try{
			var lPad    = this.getElementsComputedStyle(this._suggestionsDiv, "paddingLeft",      "padding-left");
			var rPad    = this.getElementsComputedStyle(this._suggestionsDiv, "paddingRight",     "padding-right");
			var lBorder = this.getElementsComputedStyle(this._suggestionsDiv, "borderLeftWidth",  "border-left-width");
			var rBorder = this.getElementsComputedStyle(this._suggestionsDiv, "borderRightWidth", "border-right-width");
			lPad    = isNaN(lPad)    ? 0 : lPad;
			rPad    = isNaN(rPad)    ? 0 : rPad;
			lBorder = isNaN(lBorder) ? 0 : lBorder;
			rBorder = isNaN(rBorder) ? 0 : rBorder;
			return parseInt(lPad) + parseInt(rPad) + parseInt(lBorder) + parseInt(rBorder);
		}
		catch (e){
			// safari sometimes kicks off so..
			return 0;
		}
    },
    
    // extracts the actual numeric value of a specified css style attribute
    getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
      if ( arguments.length == 2 )
         mozillaEquivalentCSS = cssProperty;
      if ( htmlElement.currentStyle )
         return htmlElement.currentStyle[cssProperty];
      else
         return document.defaultView.getComputedStyle(htmlElement, null).getPropertyValue(mozillaEquivalentCSS);
    },
    
    /// <summary>
    /// Called when the web service compeltes it's call
    /// </summary>
    /// <returns />
    _onMethodComplete : function(result, userContext, methodName) {
        // Success, create the popup
        this._suggestions = result;
        // notify any listerners?
    	if(this._onSearchCompleteClientFunction) {
    	    eval((this._onSearchCompleteClientFunction + "('" + this._lastRequestString.gsub(/["']/,'') + "'," + this._suggestions.length + ")"));
    	}
        this.onSearchComplete(true);
    },

    /// <summary>
    /// Called when the web service returns a failure code.
    /// </summary>
    /// <returns />
    _onMethodError : function(webServiceError, userContext, methodName) {
        // reset control
        this.reset();
    },
    
    /// Handles the keydown event in the associated textbox.
    onKeyDown: function(evt) {
		var upArrow   = 38;
		var downArrow = 40;
		var escapeKey = 27;
		var stopEvent = false;
		this.cancelEvent(evt);
        if (evt.keyCode == upArrow) {
            stopEvent = true;
			this.moveSelectionUp(evt);
		}
		else if (evt.keyCode == downArrow){
		    stopEvent = true;
			this.moveSelectionDown(evt);
			
		}
		else if (evt.keyCode == escapeKey) {
		    stopEvent = true;
		    
		    if (this._suggestionsVisible == true) {
		        this.hideSuggestions();
		    }
		    else {
		        this.reset();
		    }
		}
		
		// cancel event propagation?
		if(stopEvent && evt.preventDefault) {
		   evt.preventDefault();
		}		
	},
	
    /// Handles the keyup event in the associated textbox.
	onKeyUp: function(evt) {
	    var e = this.get_element();
	    this.cancelEvent(evt);
		if (e.length == 0 && e.IsOpera) {
			this.hideSuggestions();
		}
		if (!this.handledSpecialKeys(evt)) {
			this.handleTextInput(false);
		}
	},
	
	// prevents event propagation in a cross browser way.
	cancelEvent: function(evt) {
        if (evt.preventDefault) {
            evt.stopPropagation();
        } 
        else {
            evt.cancelBubble = true;
        }
    },
	
	// initiates a seach when the text field recieves focus
	// with existing text
	onFocus: function(evt) {
        // inititiate a search
	    this.handleTextInput(true);
    },
    
    /// Determines whether or not the user has navigation, selection
    /// or alphanumeric key and initiates select command, if necessary.
	handledSpecialKeys: function(evt) {
		var enterKey  = 13;
		var escapeKey = 27;
		var upArrow   = 38;
		var downArrow = 40;
		var leftArrow = 37;
		var rightArrow = 39;
		var c = evt.keyCode;	// saves typing

		if (c == upArrow || c == downArrow || c == leftArrow || c == rightArrow) {
			return true;
		}
		else if (c == enterKey) {
			this.setInputFromSelection();
			return true;
		}
		else if (c == escapeKey) {
		    if (this._suggestionsVisible == true) {
		        this.hideSuggestions();
		    }
		    else {
		        this.reset();
		    }
		    
		    return true;
		}
		
		return false;
	},
    
    /// Handles the onblur event in the associated textbox.
	onBlur: function(evt) {
        if (this._suggestionsVisible == true && this._isSuggestionsDivFocused == false) {
	        this.setInputFromSelection();
      	    this.hideSuggestions();
        }
        
	},
	
	// Indicates whether or not the suggestions are displayed in tabular format
  	isTabular: function() {
  		if(this._format == 2) {
  			return true;
  		}
  		else {
  			return false;
  		}
  	},
  	
  	// adds a custom parameter to the internal collection. This will
  	// appear in the 'additionalParameters' parameter in the web service.
  	addParameter: function(paramName, paramValue) {
  	    this._additionalParameters[paramName] = paramValue;
  	},
	
    /// <value type="String">
    /// Suggestion identifier
    /// </value>
    get_SelectedValue : function() {
        return this.get_ClientState().SelectedValue;
        //return this._hiddenInput.value;
    },
    set_SelectedValue : function(value) {
        this._hiddenInput.value = value;
        this.get_ClientState().SelectedValue = value;
        this.SaveClientStateField();
    },
    
    /// <summary>
    /// Search box text.
    /// </summary>
    get_SelectedText : function() { 
        return this.get_ClientState().SelectedText;
    },
    set_SelectedText : function(value) {
        // update the display
        this.get_element().value = value;
        // update the viewstate 
        this.get_ClientState().SelectedText = value;
        this.SaveClientStateField();
    },
    
    /// <summary>
    /// Maximum number of records to return.
    /// </summary>
    get_MaxRecords : function() { 
        return this._maxRecords;
    },
    set_MaxRecords : function(value) { 
        this._maxRecords = value;
    },
    
     /// <summary>
    /// Maximum number of suggestions from the search that are actually displayed to the user.
    /// </summary>
    get_MaxDisplayed : function() { 
        return this._maxDisplayed;
    },
    set_MaxDisplayed : function(value) { 
        this._maxDisplayed = value;
    },
    
    /// <summary>
    /// Gets and sets the classname of the popup suggestion div.
    /// </summary>
    get_SuggestionClassName : function() { 
        return this._suggestionClassName;
    },
    set_SuggestionClassName : function(value) { 
        this._suggestionClassName = value;
    },
        
    /// <summary>
    /// Gets and sets the classname of the popup suggestion container div.
    /// </summary>
    get_SuggestionContainerClassName : function() { 
        return this._suggestionContainerClassName;
    },
    set_SuggestionContainerClassName : function(value) { 
        this._suggestionContainerClassName = value;
    },
    
            
    /// <summary>
    /// Gets and sets the title text of the popup suggestion div.
    /// </summary>
    get_SuggestionTitleText : function() { 
        return this._suggestionTitleText;
    },
    set_SuggestionTitleText : function(value) { 
        this._suggestionTitleText = value;
    },
    
    /// <value type="String" mayBeNull="true">
    /// Path to the web service
    /// </value>
    get_ServicePath : function() {
        return this._servicePath;
    },
    set_ServicePath : function(value) {
            this._servicePath = value;
    },

    /// <value type="String">
    /// Name of the method to invoke on the web service
    /// </value>
    get_ServiceMethod : function() {
        return this._serviceMethod;
    },
    set_ServiceMethod : function(value) {
        this._serviceMethod = value;
    },
    
    /// <value type="String">
    /// Client On Begin Request Event Handler
    /// </value>
    get_OnBeginSearchClientFunction : function() {
        return this._onBeginSearchClientFunction;
    },
    set_OnBeginSearchClientFunction : function(value) {
        this._onBeginSearchClientFunction = value;
    },
    
    /// <value type="String">
    /// Client On Complete Request Event Handler
    /// </value>
    get_OnSearchCompleteClientFunction : function() {
        return this._onSearchCompleteClientFunction;
    },
    set_OnSearchCompleteClientFunction : function(value) {
        this._onSearchCompleteClientFunction= value;
    },
    /// <value type="String">
    /// Client user select Event Handler
    /// </value>
    get_OnSelectedItemChangedClientFunction : function() {
        return this._onSelectedItemChangedClientFunction;
    },
    set_OnSelectedItemChangedClientFunction : function(value) {
        this._onSelectedItemChangedClientFunction = value;
    },
    /// <value type="String">
    /// Client user custom value Event Handler
    /// </value>
    get_OnCustomValueEnteredClientFunction : function() {
        return this._onCustomValueEnteredClientFunction;
    },
    set_OnCustomValueEnteredClientFunction : function(value) {
        this._onCustomValueEnteredClientFunction = value;
    },
     // gets and sets the message displayed when a search returns no matches
	set_FreeCharacters: function(value) {
		this._freeCharacters = parseInt(value);
	},

	get_FreeCharacters: function() {
		return this._freeCharacters;
    },
    
    // gets and sets whether or not the search matches all or part of the string
	set_MatchAnywhere: function(value) {
		this._matchAnywhere = value;
	},

	get_MatchAnywhere: function() {
		return this._matchAnywhere;
    },
    // gets and sets whether or not the search is case sensitive
	set_IsCaseSensitive: function(value) {
		this._isCaseSensitive = value;
	},

	get_IsCaseSensitive: function() {
		return this._isCaseSensitive;
    },
    
    // gets and sets whether or not search results are cached on the client.
	set_CacheSearch: function(value) {
		this._cacheSearch = value;
	},

	get_CacheSearch: function() {
		return this._cacheSearch;
    },
    
    // gets and sets the popup suggestons style (list or table).
	set_Format: function(value) {
	    this._format=value;
	},

	get_Format: function() {
		return this._format;
    },
    // gets and sets where the suggestions are displayed relative to the textbox.
	set_PopupPosition: function(value) {
	    this._popupPosition=value;
	},

	get_PopupPosition: function() {
		return this._popupPosition;
    },
    
    // gets and sets the message displayed when search returns no matches.
	set_NoMatchMessage: function(value) {
	    this._noMatchMessage=value;
	},

	get_ContainerId: function() {
		return this._containerId;
    },
    
    // Gets or sets the id of any container that centers the page using a width attribute and auto left and right margins.
	set_ContainerId: function(value) {
	    this._containerId=value;
	},

	get_NoMatchMessage: function() {
		return this._noMatchMessage;
    },
    // Determines whether or not a no match message is displayed if the search returns no matches.
	set_ShowNoMatch: function(value) {
	    this._showNoMatch=value;
	},

	get_ShowNoMatch: function() {
		return this._showNoMatch;
    },
    
    // Determines whether or the popup is displayed with
    // the same width as the associated textbox, or allowed to sized by css.
	set_MatchTextWidth: function(value) {
	    this._matchTextWidth=value;
	},

	get_MatchTextWidth: function() {
		return this._matchTextWidth;
    },
    
    // sets the template that determines which columns are
    // displayed in the text field after user selects a suggestion in table mode.
    set_SelectedItemDisplayTemplate: function(value) {
        this._selectedItemDisplayTemplate = value;
    },
    
    get_SelectedItemDisplayTemplate: function() {
        return this._selectedItemDisplayTemplate;
    },
    
    // Determines whether or not the control initiates a search when
    // it receives focus and has existing text.
	set_SearchOnFocus: function(value) {
	    this._searchOnFocus=value;
	},

	get_SearchOnFocus: function() {
		return this._searchOnFocus;
    },
    
    /// <summary>
    /// The colour of the highlighted list item.
    /// </summary>
    get_SelectionColour : function() {
        return this._selectionColour;
    },
    set_SelectionColour : function(value) {
        this._selectionColour = value;
    },
    
    /// <summary>
    /// The colour of the highlighted text list item.
    /// </summary>
    get_SelectionTextColour : function() {
        return this._selectionTextColour;
    },
    set_SelectionTextColour : function(value) {
        this._selectionTextColour = value;
    },
    
    /// <summary>
    /// The maximum height to display until the scrolling is enabled.
    /// </summary>
    get_MaxHeightBeforeScrolling : function() {
        return this._maxHeightBeforeScrolling;
    },
    set_MaxHeightBeforeScrolling : function(value) {
        this._maxHeightBeforeScrolling = value;
    },

    // gets and sets whether or not the search matches all or part of the string
	set_DisplayInnerContainer: function(value) {
		this._displayInnerContainer = value;
	},

	get_DisplayInnerContainer: function() {
		return this._displayInnerContainer;
    }
}

Controls.AutoSuggestTextBoxBehavior.registerClass('Controls.AutoSuggestTextBoxBehavior', Controls.ScriptControl);

/************************************************************************************************/
/* AutoSuggest - Static Members and Properties                                                  */
/************************************************************************************************/

/// <summary>
///Static member - an array that hold references to all the autosuggest pages on a page.
/// <summary>
Controls.AutoSuggestTextBoxBehavior.registeredInstances = new Array();

/// <summary>
///Static Member  - number of autosuggest controls registered.
/// <summary>
Controls.AutoSuggestTextBoxBehavior.registeredInstancesCount = 0;

/// <summary>
/// Registers the autosuggest control and allows for repositioning of the suggestion div when the window is resized
/// <summary>
Controls.AutoSuggestTextBoxBehavior.registerInstance = function(autosuggest){

    Controls.AutoSuggestTextBoxBehavior.registeredInstances[Controls.AutoSuggestTextBoxBehavior.registeredInstancesCount] = autosuggest;
    Controls.AutoSuggestTextBoxBehavior.registeredInstancesCount++;
}

/// <summary>
/// Repositions the suggest div
/// <summary>
Controls.AutoSuggestTextBoxBehavior.repositionSuggestionDivs = function(){
    for(var index = 0; index < Controls.AutoSuggestTextBoxBehavior.registeredInstances.length; index++ ) {    
            var asInstance = Controls.AutoSuggestTextBoxBehavior.registeredInstances[index];
            if (asInstance._suggestionsVisible == true) {
                asInstance.hideSuggestions();  
                asInstance.showSuggestions();  
            }
            asInstance = null;
        }
}

/************************************************************************************************/
/* Data Entry Container Item Behaviour                                                          */
/************************************************************************************************/

Controls.DataEntryContainerItemBehavior = function(element)
{
    Controls.DataEntryContainerItemBehavior.initializeBase(this, [element]);
    this._hidden = false;
}

Controls.DataEntryContainerItemBehavior.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.DataEntryContainerItemBehavior.callBaseMethod(this, 'initialize');   
        this.Hidden(this.get_Hidden());              
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {
        Controls.DataEntryContainerItemBehavior.callBaseMethod(this, 'dispose');
    },
   
    Hidden : function(hidden) {
        var e = this.get_element();

        if(hidden)
        {
            e.style.display = 'none';
        }
        else
        {
            e.style.display = 'block';  
        }

        for(var i = 0; i <= (Page_Validators.length - 1); i++)
        {
            if(this.isValidatorInContainer(Page_Validators[i]))
            {
                ValidatorEnable(Page_Validators[i], !hidden);
            }
        }
    },
    
    isValidatorInContainer : function(validator) { 
        var e = this.get_element();
        var id = e.id;
        
        var parent = validator.parentNode
        
        while(parent != null)
        {
            if(parent.id == id)
            {
                return true;
            }
            parent = parent.parentNode
            if(parent.tagName.toUpperCase() == "BODY")
            {
                parent = null;
            }
        }
        return false;
    },
            
    /// <value type="String">
    /// Weather the data entry container item is hidden
    /// </value>
    get_Hidden : function() {
        return this._hidden;
    },
    set_Hidden : function(value) {
        this._hidden = value;
    }
}

Controls.DataEntryContainerItemBehavior.registerClass('Controls.DataEntryContainerItemBehavior', Controls.ScriptControl);


/************************************************************************************************/
/* Rating Indicator Control Behaviour                                                             */
/************************************************************************************************/
function RatingIndicatorClientCustomValidator(sender, args) {
	args.IsValid = true;		// Default - if client validation fails, validation will be delegated to the server-side code.
	if (sender && (sender.parentElement || sender.parentNode)) {
        var parent = sender.parentElement || sender.parentNode;
        var containerDivId = parent.id;	
		var client = window[containerDivId + "Client"];
		if (client && client.get_Required()) {
			var clientRating = client.get_SelectedValue();
			if (isNaN(clientRating) || (clientRating <= 0 || clientRating > (client._colourList.length + 1)))
				args.IsValid = false;
		}
	}
	return args.IsValid;
}

Controls.RatingIndicatorControlBehaviour = function(element) {
	Controls.RatingIndicatorControlBehaviour.initializeBase(this, [element]);
	
	// Property variables.
	this._clientOnSelectedValueChanged = null;
	this._colourList = null;
	this._imageRoot = null;
	this._hasEndImage = false;
	this._hasStartImage = false;
	this._indicatorType = null;
	this._readOnly = false;
	this._required = false;
	this._roundingType = "roundup";
	this._scoreTextPrefix = null;
	this._scoreTextVisible = false;
	this._selectedValue = 0;
	
	// Nested/monitored fields/field ID's.
	this._scoreTextField = null;
	this._images = [];
	
	// Constants.
	this.DefaultColour = "unselected";
	this.ImageExtension = ".png";
	this.StartImageFileName = "start" + this.ImageExtension;
	this.EndImageFileName = "end" + this.ImageExtension;
	this.ReadOnlyFilePrefix = "rating";
	
	// Monitoring values.
	this._initialised = false;
	this._interfaceLocked = false;
}

Controls.RatingIndicatorControlBehaviour.prototype = {
	/// <summary>
    /// Initialise Function
    /// </summary>
    initialize : function() {
		Controls.RatingIndicatorControlBehaviour.callBaseMethod(this, 'initialize');  
		
		var element = this.get_element();
		this._clientId = element.id;
		
		this.interfaceInitialise();
		this.addMouseHandlers();
		
		window[element.id + "Client"] = this;
		
		this._initialised = true;
    },
    
    /// <summary>
    /// Dispose Function
    /// </summary>
    dispose : function() {
		this.removeMouseHandlers();
		Controls.RatingIndicatorControlBehaviour.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>Raised when the user clicks on an element in this control's container.</summary>
    elementClick: function(eventElement) {
		if (!this._readOnly) {
			var clickRating = this.getRatingFromEvent(eventElement);
			if (clickRating > 0) {
				var rating = this.get_SelectedValue();
				this.set_SelectedValue(clickRating);
				this.raiseSelectedValueChanged(rating, clickRating);
			}
		}
    },
    
    /// <summary>Raised when the mouse moves over an element in this control's container.</summary>
    elementMouseOver: function(eventElement) {
		if (!this._readOnly) {
			var hoverRating = this.getRatingFromEvent(eventElement);
			if (hoverRating > 0)
				this.displayRating(hoverRating);
		}
    },
    
    /// <summary>Raised when the mouse moves out of an element in this control's container.</summary>
    elementMouseOut: function(eventElement) {
		if (!this._readOnly) {
			this.refresh();
		}
    },
    
    /// <summary>Attaches the mouse event handlers to the container.</summary>
    addMouseHandlers: function() {
		var element = this.get_element();
		this._mouseOverHandler = Function.createDelegate(this, this.elementMouseOver);
		$addHandler(element, "mouseover", this._mouseOverHandler);
		
		this._mouseOutHandler = Function.createDelegate(this, this.elementMouseOut);
		$addHandler(element, "mouseout", this._mouseOutHandler);
		
		this._clickHandler = Function.createDelegate(this, this.elementClick);
		$addHandler(element, "click", this._clickHandler);
    },
    
    /// <summary>Checks if the interface should be recreated based on the values provided.</summary>
    checkShouldResetInterface: function(oldValue, newValue) {
		return (this._initialised && !this._interfaceLocked && oldValue != newValue);
    },
    
    /// <summary>Creates and appends an image to the provided element</summary>
	createAndAppendImage: function(imageId, imageFileName, element, alt, title) {
		var image = this.createImage(this._clientId + imageId, this._imageRoot + "/" + imageFileName, alt, title);
		this._images.push(image.id);
		element.appendChild(image);
	},
    
    /// <summary>Cross-browser compatible runtime image creation.</summary>
    createImage: function(id, imageSource, alt, title) {
		var image;
		if (document.all) {
			// IE doesn't support the setting of the "id" property via the use of setAttribute; have to pass the full element string.
			// Non-IE browsers don't support the passing of the full element string.
			image = document.createElement("<img src=\"" + imageSource + "\" id=\"" + id + "\" />");
		} else {
			image = document.createElement("img");
			image.setAttribute("id", id);
			image.setAttribute("src", imageSource);
		}
		image.setAttribute("alt", alt || "");
		image.setAttribute("title", title || "");
		
		return image;
    },
    
    /// <summary>Updates the displayed read/write rating value.</summary>
	displayRating: function(rating) {
		var compareRating = this.roundRating(rating);
		var ratingColour = (compareRating > 0 && compareRating <= this._colourList.length) ? this._colourList[compareRating - 1] : this.DefaultColour;
		var startOffset = (this._hasStartImage ? 1: 0);
		var endOffset = (this._hasEndImage ? 1: 0);
    
    
		for (var i = startOffset, max = (this._images.length - endOffset); i < max; i++) 
		{		    
		    var image = document.getElementById(this._images[i]);
		    
            // Statement to handle the fivestarswithhalf enum
		    if (this._indicatorType == "FiveStarsWithHalf")
		    {	
                if((i/2) < rating && rating > 0)
                {
                    if(!(i%2))
                    {
                        image.src = image.src.substring(0, image.src.lastIndexOf("/") + 1) + "starleft" + this.ImageExtension;
                    }
                    else
                    {
                        image.src = image.src.substring(0, image.src.lastIndexOf("/") + 1) + "starright" + this.ImageExtension;
                    }
                }
                else
                {
                    if(!(i%2))
                    {
                        image.src = image.src.substring(0, image.src.lastIndexOf("/") + 1) + "unselectedleft" + this.ImageExtension;
                    }
                    else
                    {
                        image.src = image.src.substring(0, image.src.lastIndexOf("/") + 1) + "unselectedright" + this.ImageExtension;
                    }
                }
            }
            else
            {			    
			    var newImageFile = (((i + (1 - startOffset)) <= compareRating) ? ratingColour : this.DefaultColour) + this.ImageExtension;
			    image.src = image.src.substring(0, image.src.lastIndexOf("/") + 1) + newImageFile;
			}
		}

		this.updateScoreText(rating);
	},
    
	/// <summary>Retrieves the ID stored in the specified element's ID property.</summary>
	getElementIndexFromId: function(element) {
		var index = 0;
		if (element && element.id) 
		{
			var id = element.id;
			if (id) 
			{
                if (this._indicatorType == "FiveStarsWithHalf")
                {
                    var idRegex = new RegExp("Img(.*)$", "i");
    				var matches = idRegex.exec(id);
				    if (matches && matches.length > 0) 
				    {
					    index = parseFloat(matches[1]);
				    }

                    }
                else
                {
				    var idRegex = new RegExp("\\d+$", "i");
    				var matches = idRegex.exec(id);
				    if (matches && matches.length > 0) 
				    {
					    index = parseInt(matches[0]);
				    }
			    }				
			}
		}
		return index;
	},
	
	/// <summary>Retrieves the element that the event was raised from.</summary>
	getEventSource: function(evt) {
		if (evt) {
			evt = evt || window.event;
			return evt.target || evt.srcElement;
		}
		return null;
	},
    
	/// <summary>Retrieves the rating index from the image that was the source of the event.</summary>
	getRatingFromEvent: function(event) {
		var eventSource = this.getEventSource(event);
		var rating = 0;
		if (eventSource)
			rating = this.getElementIndexFromId(eventSource);
		return rating;
	},
	
	/// <summary>First-use initialisation for this control.</summary>
    interfaceInitialise: function() {
		var clientObject = this.get_element();
		var rating = this.roundRating(this._selectedValue);
		var displayRating = (Math.round(this._selectedValue * 10) / 10).toString();
		
        if (this._readOnly) 
        {
            if (this._indicatorType == "FiveStarsWithHalf")
            {
	            var imageFile = this.ReadOnlyFilePrefix + (displayRating) + this.ImageExtension;
	            var alt = "Rated " + (displayRating) + " / " + (this._colourList.length/2);
            }
            else
            {
	            var imageFile = this.ReadOnlyFilePrefix + rating.toString() + this.ImageExtension;
	            var alt = "Rated " + displayRating.toString() + " / " + this._colourList.length.toString();
			}
	        this.createAndAppendImage("Rating", imageFile, clientObject, alt, alt);
        } 
        else 
        {
	        if (this._hasStartImage && !this._readOnly)
		        this.createAndAppendImage("Start", this.StartImageFileName, clientObject);
			
	            var ratingColour = (rating > 0 && rating <= this._colourList.length) ? this._colourList[rating - 1] : this.DefaultColour;
	            for (var i = 1, max = this._colourList.length; i <= max; i++) 
	            {
                    // Statement to handle the fivestarswithhalf enum
	                if (this._indicatorType == "FiveStarsWithHalf")
	                {
                        if((i/2) <= displayRating)
                        {
                            if((i%2))
                            {
                                var imageFile = "starleft"  + this.ImageExtension;
                            }
                            else
                            {
                                var imageFile = "starright" + this.ImageExtension;
                            }
                        }
                        else
                        {
                            if((i%2))
                            {
                                var imageFile = "unselectedleft"  + this.ImageExtension;
                            }
                            else
                            {
                                var imageFile = "unselectedright" + this.ImageExtension;
                            }
                        }
	                    var alt = (i/2).toString() + " / " + (max/2).toString();
	                    this.createAndAppendImage("Img" + (i/2).toString(), imageFile, clientObject, alt, alt);
	                }
                    else
                    {
		                var imageFile = ((i <= rating) ? ratingColour : this.DefaultColour) + this.ImageExtension;
	                    var alt = (i).toString() + " / " + max.toString();
	                    this.createAndAppendImage("Img" + (i).toString(), imageFile, clientObject, alt, alt);
    			    }
                }
			
	        if (this._hasEndImage && !this._readOnly)
		        this.createAndAppendImage("End", this.EndImageFileName, clientObject);
        }
		
		if (this._scoreTextField == null) 
		{
			this._scoreTextField = document.createElement("p");
            if (this._indicatorType == "FiveStarsWithHalf")
            {
    	        this._scoreTextField.innerHTML = this._scoreTextPrefix + displayRating + "/" + (this._colourList.length / 2).toString();
            }
            else
            {
			    this._scoreTextField.innerHTML = this._scoreTextPrefix + displayRating + "/" + this._colourList.length.toString();
			}
			this._scoreTextField.style.display = ((this._scoreTextVisible) ? "" : "none");
			
			clientObject.appendChild(this._scoreTextField);
		}
    },
    
    /// <summary>Raises client-side & server side events for when the rating changed.</summary>
    raiseSelectedValueChanged: function(oldValue, newValue) {
		if (this._clientOnSelectedValueChanged != null) {
			var args = { oldValue: oldValue, newValue: newValue };
			if (typeof this._clientOnSelectedValueChanged == "function") {
				this._clientOnSelectedValueChanged(this, args);
			} else {
				eval(this._clientOnSelectedValueChanged + "(this, args)");
			}
		}
    },
    
    /// <summary>Refreshes the current interface.</summary>
    refresh: function() 
    {
	    this.displayRating(this._selectedValue);
    },
    
    /// <summary>Removes the mouse handlers from the container.</summary>
    removeMouseHandlers: function() {
		var element = this.get_element();
		if (this._mouseOverHandler) {
			$removeHandler(element, "mouseover", this._mouseOverHandler);
			this._mouseOverHandler = null;
		}
		
		if (this._mouseOutHandler) {
			$removeHandler(element, "mouseout", this._mouseOutHandler);
			this._mouseOutHandler = null;
		}
		
		if (this._clickHandler) {
			$removeHandler(element, "click", this._clickHandler);
			this._clickHandler = null;
		}
    },
    
    /// <summary>Rebuilds the user interface.</summary>
    resetInterface: function() {
		if (this._images && this._images.length > 0) {
			var element = this.get_element();
			for (var i = 0, max = this._images.length; i < max; i++) {
				var image = $get(this._images[i]);
				if (image) {
					try {
						element.removeChild(image);
					} catch (e) {}
				}
			}
			this._images = [];
		}
		
		if (this._scoreTextField) {
			element.removeChild(this._scoreTextField);
			this._scoreTextField = null;
		}
		
		this.interfaceInitialise();
    },
	
	/// <summary>Rounds the rating value based on the "RoundingType" property.</summary>
	roundRating: function(rating) {
		if (this._roundingType == "roundup")
			rating = Math.ceil(rating);
		else
			rating = Math.floor(rating);
		return rating;
	},  
    
    /// <summary>Updates the score text</summary>
    updateScoreText: function(rating) 
    {
		if (this._scoreTextField)
		{
            if (this._indicatorType == "FiveStarsWithHalf")
            {
    	        this._scoreTextField.innerHTML = this._scoreTextPrefix + (rating) + "/" + (this._colourList.length/2).toString();
            }
            else
            {
			    this._scoreTextField.innerHTML = this._scoreTextPrefix + (Math.round(rating * 10) / 10).toString() + "/" + this._colourList.length.toString();
            }		
	    }		
    },
            
    /// <summary>Gets or sets the name of the client-side function to call when the user changes the selected rating value.</summary>
    get_ClientOnSelectedValueChanged: function() {
		return this._clientOnSelectedValueChanged;
    },
    set_ClientOnSelectedValueChanged: function(value) {
		this._clientOnSelectedValueChanged = value;
    },
    
    /// <summary>Gets or sets the list of colours per rating.</summary>
	get_ColourList: function() {
		return this._colourList;
	},
	set_ColourList: function(value) {
		this._colourList = value;
	},
	
    /// <summary>Gets or sets the expected image root.</summary>
	get_ImageRoot: function() {
		return this._imageRoot;
	},
	set_ImageRoot: function(value) {
		if (value && value.substring(value.length - 1) == "/")
			value = value.substring(0, value.length - 1);
		this._imageRoot = value;
	},
	
    /// <summary>Gets or sets the selected indicator style.</summary>
	get_IndicatorType: function() {
		return this._indicatorType;
	},
	set_IndicatorType: function(value) {
		this._indicatorType = value;
	},
	
    /// <summary>Gets or sets if the indicator uses a special end image.</summary>
	get_HasEndImage: function() {
		return this._hasEndImage;
	},
	set_HasEndImage: function(value) {
		this._hasEndImage = value;
	},
	
    /// <summary>Gets or sets if the indicator uses a special start image.</summary>
	get_HasStartImage: function() {
		return this._hasStartImage;
	},
	set_HasStartImage: function(value) {
		this._hasStartImage = value;
	},
	
    /// <summary>Gets or sets if the control is read-only.</summary>
	get_ReadOnly: function() {
		return this._readOnly;
	},
	set_ReadOnly: function(value) {
		var shouldResetInterface = (this._readOnly != value && this._initialised);
		this._readOnly = value;
		
		if (shouldResetInterface)
			this.resetInterface();
	},
	
    /// <summary>Gets or sets if this control is required.</summary>
	get_Required: function() {
		return this._required;
	},
	set_Required: function(value) {
		this._required = value;
	},
	
	/// <summary>Gets or sets the manner in which the control will handle floating values when generating the interface.</summary>
	get_RoundingType: function() {
		return this._roundingType;
	},
	set_RoundingType: function(value) {
		this._roundingType = value;
	},
	
    /// <summary>Gets or sets the score text prefix.</summary>
	get_ScoreTextPrefix: function() {
		return this._scoreTextPrefix;
	},
	set_ScoreTextPrefix: function(value) {
		this._scoreTextPrefix = value;
		this.updateScoreText(this._selectedValue);
	},
	
    /// <summary>Gets or sets if the score text field will be displayed.</summary>
	get_ScoreTextVisible: function() {
		return this._scoreTextVisible;
	},
	set_ScoreTextVisible: function(value) {
		this._scoreTextVisible = value;
		if (this._scoreTextField)
			this._scoreTextField.style.display = (value ? "" : "none");
	},
	
    /// <summary>Gets or sets the selected rating value.</summary>
	get_SelectedValue: function() {
		return this._selectedValue;
	},
	set_SelectedValue: function(value) {
		this.get_ClientState().SelectedValue = this._selectedValue = value;
		this.SaveClientStateField();
	}
}

Controls.RatingIndicatorControlBehaviour.registerClass('Controls.RatingIndicatorControlBehaviour', Controls.ScriptControl);

/************************************************************************************************/
/* Multi-line Textbox Control Behaviour                                                             */
/************************************************************************************************/
Controls.MultilineTextBoxBehavior = function(element) {
	Controls.MultilineTextBoxBehavior.initializeBase(this, [element]);

	this._averageWordLength = 6.1;
	this._characterCountVisible = false;
	this._displayMessage = "";
	this._maxLength = 0;
	this._promptText = "";
	this._wordCountVisible = false;

	this._statusField = null;
	this._statusFieldId = "";
}

Controls.MultilineTextBoxBehavior.prototype = {
	/// <summary>
	/// Initialises this instance.
	/// </summary>
	initialize: function() {
		Controls.MultilineTextBoxBehavior.callBaseMethod(this, "initialize");
		var e = this.get_element();
		
		this._updateAndRestrictHandler = Function.createDelegate(this, this._updateAndRestrict);
		$addHandler(e, "blur", this._updateAndRestrictHandler);
		$addHandler(e, "keyup", this._updateAndRestrictHandler);
		$addHandler(e, "keydown", this._updateAndRestrictHandler);
		$addHandler(e, "change", this._updateAndRestrictHandler);
		
		if (this._statusFieldId) {
			this._statusField = $get(this._statusFieldId);
			
			if (this._shouldUpdateStatus()) {
				// Default the value displayed...
				this._updateStatusField(this._trueTextLength(e), this._valueIsPromptText(e.value));
			} else {
				this._statusField.style.display = "none";
			}
		}
	},

	/// <summary>
	/// Releases the resources used by this instance.
	/// </summary>
	dispose: function() {
		var e = this.get_element();
		
		if (this._updateAndRestrictHandler) {
			$removeHandler(e, "blur", this._updateAndRestrictHandler);
			$removeHandler(e, "keyup", this._updateAndRestrictHandler);
			$removeHandler(e, "keydown", this._updateAndRestrictHandler);
			$removeHandler(e, "change", this._updateAndRestrictHandler);
			
			this._updateAndRestrictHandler = null;
		}
		
		Controls.MultilineTextBoxBehavior.callBaseMethod(this, "dispose");
	},
	
	/// <summary>Raised when the text has been changed and the control needs to update the status field,
	/// and remove content beyond the max length.</summary>
	_updateAndRestrict: function(evt) {
		var element = this.get_element();
		if (element && typeof element.value != "undefined") {
			var trueLength = this._trimToMaxLength(element, this._trueTextLength(element));
			this._updateStatusField(trueLength, this._valueIsPromptText(element.value));
		}
	},
	
	/// <summary>
	/// Returns true if the control has been flagged for status updates, and has a label field to update.
	/// </summary>
	_canUpdateStatus: function() {
		return (this._shouldUpdateStatus() && this._statusField != null);
	},

	/// <summary>
	/// Replaces the placeholder values in the provided message string.
	/// </summary>
	_replacePlaceholders: function(message, maxChars, charsRemaining, wordsRemaining) {
		if (maxChars != null)
			message = message.replace("[[MaxCharacters]]", maxChars.toString());
		if (charsRemaining != null)
			message = message.replace("[[CharacterCount]]", charsRemaining.toString());
		if (wordsRemaining != null)
			message = message.replace("[[WordCount]]", wordsRemaining.toString());
		return message;
	},
	
	/// <summary>
	/// Returns true if the control has been flagged for status updates.
	/// </summary>
	_shouldUpdateStatus: function() {
		return (this._maxLength > 0 && (this._characterCountVisible || this._wordCountVisible));
	},
	
	/// <summary>Trims the contents of the text field to the maximum length.</summary>
	_trimToMaxLength: function(e, calculatedLength) {
		if (this._maxLength > 0 && calculatedLength > this._maxLength) {
			var trimLength = 0;
			
			while (calculatedLength > this._maxLength) {
				var lastChars = e.value.substring(e.value.length - 2);
				var trimCount = (lastChars == "\r\n") ? 2 : 1;
				trimLength += trimCount;
				calculatedLength -= trimCount;
			}
			e.value = e.value.substring(0, calculatedLength);
			
			if (typeof e.scrollTop != "undefined" && typeof e.scrollHeight != "undefined")
				e.scrollTop = e.scrollHeight;
		}
		return calculatedLength;
	},
	
	/// <summary>Calculates the true length of the element's value by removing the dependency on CRLF values.</summary>
	_trueTextLength: function(element) {
		var value = element.value;
		var length = value.length;
		if (length > 0) {
			if (value.match(/(\r\n)/ig)) {
				var lengthNoCrLf = value.replace(/[\f\n\r]*/ig, "").length;
				var crLfCount = (length - lengthNoCrLf) / 2;
				length -= crLfCount;
			}
		}
		
		return length;
	},
	
	///<summary>If the character or word counts are enabled, updates the status field content.</summary>
	_updateStatusField: function(trueLength, onlyPromptText) {
		if (this._canUpdateStatus()) {
			var charsLeft = charsLeft = (onlyPromptText) ? this._maxLength : this._maxLength - trueLength;
			var wordsLeft = Math.round(((charsLeft / this._averageWordLength) * 10)) / 10;

			this._statusField.innerHTML = this._replacePlaceholders(this._displayMessage, this._maxLength, charsLeft, wordsLeft);
		}
	},
	
	/// <summary>Returns true if the value specified is the prompt text for the field.</summary>
	_valueIsPromptText: function(value) {
		return (value != null && this._promptText != null 
			&& this._promptText.length > 0 
			&& value.toLowerCase() == this._promptText.substring(0, this._maxLength).toLowerCase());
	},
	
	/// <summary>Average word length</summary>
	get_AverageWordLength: function() {
		return this._averageWordLength;
	},
	set_AverageWordLength: function(value) {
		this._averageWordLength = value;
	},

	/// <summary>Character count is visible</summary>
	get_CharacterCountVisible: function() {
		return this._characterCountVisible;
	},
	set_CharacterCountVisible: function(value) {
		this._characterCountVisible = value;
	},

	/// <summary>Status display message</summary>
	get_DisplayMessage: function() {
		return this._displayMessage;
	},
	set_DisplayMessage: function(value) {
		this._displayMessage = value;
	},
	
	/// <summary>Maximum length permitted</summary>
	get_MaxLength: function() {
		return this._maxLength;
	},
	set_MaxLength: function(value) {
		this._maxLength = value;
	},
	
	/// <summary>Gets or sets the prompt text that will be displayed in the textbox.</summary>
	get_PromptText: function() {
		return this._promptText;
	},
	set_PromptText: function(value) {
		this._promptText = value;
	},
	
	/// <summary>Gets or sets the ID of the status field control.</summary>
	get_StatusFieldId: function() {
		return this._statusFieldId;
	},
	set_StatusFieldId: function(value) {
		this._statusFieldId = value;
	},
	
	/// <summary>Word count is visible.</summary>
	get_WordCountVisible: function() {
		return this._wordCountVisible;
	},
	set_WordCountVisible: function(value) {
		this._wordCountVisible = value;
	}
}

Controls.MultilineTextBoxBehavior.registerClass('Controls.MultilineTextBoxBehavior', Controls.ScriptControl);

/************************************************************************************************/
/* Lookup Behavior                                                                              */
/************************************************************************************************/

Controls.LookupBehavior = function(element)
{
    Controls.LookupBehavior.initializeBase(this, [element]); 
    this._hiddenFieldId = '';
    this._requiredFieldId = '';
    this._textBoxId = '';
    this._selectionVisibleFieldId = '';
    this._containerId = '';
    this._listLabel = '';
    this._asteriskElementTarget = '';
    this._searchButtonText = '';
    this._button;
    this._rowsToDisplay = '';
    this._internalValidationGroup = '';
    this._servicePath = '';
    this._serviceMethod = '';
    this._div = null;
    this._text = '';
    this._buttonClickHandler;
    this._itemClickHandler;
    this._disableEntry = false;
    this._disableSearch = false;
    this._listPromptText = '';
    this._onPreQuery = '';
    this._onPostQuery = '';
    this._required = false;
}

Controls.LookupBehavior.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.LookupBehavior.callBaseMethod(this, 'initialize');      
        this._setupControl();  
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {
        Controls.LookupBehavior.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Sets the value on the hidden field.
    /// </summary>
    /// <returns />
    _setHiddenField : function(value) {
        var hiddenField = $get(this._hiddenFieldId);
        hiddenField.value = value;
    },
    
    /// <summary>
    /// Sets the value on the hidden field.
    /// </summary>
    _setRequiredField : function(value) {
        var requiredField = $get(this._requiredFieldId);
        requiredField.value = value;
    },
    
    /// <summary>
    /// Triggers the on pre query event.
    /// </summary>
    /// <return>Parameter Object</returns>
    _triggerOnPreQueryEvent : function() {
        var paramobject = null;
        if(this._onPreQuery != null && this._onPreQuery.length > 0) {
            var paramobject = eval(this._onPreQuery + '()');
        }
        return paramobject;
    },
    
    /// Triggers the on post query event.
    /// </summary>
    _triggerOnPostQueryEvent : function(value) {
        if(this._onPostQuery != null && this._onPostQuery.length > 0) {
            eval(this._onPostQuery + '({"failed":' + value + '})');
        }
    },
    
    /// <summary>
    /// Valdiates this controls child controls.
    /// </summary>
    /// <returns>true if valid ; else false.</return>
    _validateControl : function() {
        var validationResult = true;
        
        if (typeof(Page_ClientValidate) == 'function') {
            validationResult = Page_ClientValidate(this._internalValidationGroup);
            Page_IsValid = true;
            Page_BlockSubmit = false;
        }
        return validationResult;
    },
    
    /// <summary>
    /// Sets up the control.
    /// </summary>
    _setupControl : function() {
        var container = $get(this._containerId);

        if (this._disableEntry) {
            textbox.setAttribute("disabled", "true");
        }
        
        if (!this._disableSearch) {
            var br = Msfg$createElement('br');
            container.appendChild(br);
            
            this._button = Msfg$createElement('input');
            this._button.setAttribute("type","button");
            this._button.setAttribute("value", this._searchButtonText);
            this._button.setAttribute("id", this._containerId + "_btnSubmit");
            Element.addClassName(this._button, "msfg-lookup-button");
            container.appendChild(this._button);
            
            var clearbr = Msfg$createElement('br');
            Element.addClassName(clearbr, "clear-br");
            container.appendChild(br);
            
            this._buttonClickHandler = Function.createDelegate(this, this._buttonClick);
		    $addHandler(this._button, "click", this._buttonClickHandler);
		}
		
        if (this.get_Text() != null && this.get_Text().length > 0 ) {
            $get(this.get_TextBoxId()).value = this.get_Text();
            this._button.setAttribute("disabled","true");
            this._createWaitingIcon();
            this._getData(this.get_Text());
        }
    },
    
    /// <summary>
    /// Called in response to the search
    /// button being clicked.
    /// </summary>
    _buttonClick : function() {
        
        this._destroyList();
        this._setHiddenField("false");
        this._setRequiredField("false");
        this._button.setAttribute("disabled","true");
        this.set_Value("");
        
        var validationResult = this._validateControl();
        if(validationResult) {          
            this._createWaitingIcon();
            var value = $get(this.get_TextBoxId()).value;
            this.set_Text(value);
            this._getData(value);
        } else {
            // Added to reset the hidden box when failed validation removes it
            this._button.disabled = false;
            var HiddenSelectionElement = document.getElementById(this.get_SelectionVisibleFieldId());
            HiddenSelectionElement.value = "false";
        }  
    },
    
    /// <summary>
    /// Called in response to a item being
    /// selected form the results list.
    /// </summary>
    _itemSelected : function(e) {
        if(e.target.value == "")
        {
             this.set_Value("");
            this._setRequiredField("false");
        }
        else
        {
            this.set_Value(e.target.value);
            this._setRequiredField("true");
        }
        
        // Set this to true so the validators know the list is created (incase of postback)
        document.getElementById(this.get_SelectionVisibleFieldId()).value = "true";
    },
    
    /// <summary>
    /// Create the results list.
    /// </summary>
    _createList : function(list) {
        var container = this.get_element();
        var div = Msfg$createElement('div');
        div.setAttribute("id", this._containerId + "_divSelectContainer");
        Element.addClassName(div, "msfg-lookup-list clear-br");
        container.appendChild(div);
        this._div = div;
        
        var label = Msfg$createElement('label');
        label.setAttribute("id", this._containerId + "_lblSelect");
        label.appendChild(document.createTextNode(this._listLabel + ' '));
        div.appendChild(label);
        
        // Make sure the control is required before we display asterisk
        if(this._required)
        {
            var asterisk = Msfg$createElement('em');
            asterisk.appendChild(document.createTextNode('*'));
            
            if(this._asteriskElementTarget == 1) 
            {
                label.appendChild(asterisk);
            }
        }
                
        var select = Msfg$createElement('select');
        select.setAttribute("id", this._containerId + "_cboSelect");
        select.setAttribute("size",this._rowsToDisplay);
        
        // Set this to true so the validators know the list is created
        document.getElementById(this.get_SelectionVisibleFieldId()).value = "true";
       
        if (list) {
            var selectedIndex = 0;
            
            var defaultOptionElement = new Option(this.get_ListPromptText(), "", false, false);
            select.options[select.options.length] = defaultOptionElement;
            
            for (i = 0 ; i < list.length ; i++) {
                var listItemName = list[i].Name;
                var listItemValue = list[i].Value;
                
                var selected = false;
                
                if(listItemValue == this.get_Value()) {
                    selectedIndex = i + 1;
                }
               
                var optionElement = new Option(listItemName, listItemValue, false, selected);
                select.options[select.options.length] = optionElement;
            }
            
            if(list.length == 1) {
                selectedIndex = 1;
                this.set_Value(listItemValue);
                
            }
            
            if(selectedIndex > 0) {
                this._setRequiredField("true");
            }
            select.selectedIndex = selectedIndex;
        }
      
        div.appendChild(select);
        
        if(this._asteriskElementTarget != 1) {
            div.appendChild(asterisk);
        }
        
        this._itemClickHandler = Function.createDelegate(this, this._itemSelected);
		$addHandler(select, "change", this._itemClickHandler);
    },
        
    /// <summary>
    /// Destroy the results list.
    /// </summary>
    _destroyList : function(){ 
        if(this._div != null) {
            e = this.get_element();
            e.removeChild(this._div);
            this._div = null;
        }
    },
    
    /// <summary>
    /// Create the waiting icon panel.
    /// </summary>
    _createWaitingIcon : function(){ 
        var container = this.get_element();
        var div = Msfg$createElement('div');
        Element.addClassName(div, "msfg-lookup-progresspanel");
        div.appendChild(document.createTextNode("Loading please wait..."));
        container.appendChild(div);
        this._div = div;
    },
    
    /// <summary>
    /// Destory the waiting icon panel.
    /// </summary>
    _destroyWaitingIcon : function(){ 
        if(this._div != null) {
            e = this.get_element();
            e.removeChild(this._div);
            this._div = null;
        }
    },
    
    /// <summary>
    /// Called to get the data from the web service.
    /// </summary>
    _getData : function(text) {
        if (this._servicePath && this._serviceMethod) {

            var paramObject = this._triggerOnPreQueryEvent();

            Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false,
                { value:text, parameters:paramObject},
                Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError));
        }
    },
    
    /// <summary>
    /// Called when the web service compeltes it's call
    /// </summary>
    /// <returns />
    _onMethodComplete : function(result, userContext, methodName) {
        this._destroyWaitingIcon();
        this._button.disabled = false;
        if(result.length > 0 ) {
            this._triggerOnPostQueryEvent(false);
            this._createList(result);
        } else {
            this._triggerOnPostQueryEvent(true);
            this._setHiddenField("true");
            var validationResult = this._validateControl();
        }
    },

    /// <summary>
    /// Called when the web service faileds it's call
    /// </summary>
    /// <returns />
    _onMethodError : function(webServiceError, userContext, methodName) {
        this._triggerOnPostQueryEvent(true);
        this._destroyWaitingIcon();
        this._button.disabled = false;
        this._setHiddenField("true");
        var validationResult = this._validateControl();
    },
        
    /// <value type="String">
    /// Get or Set Hidden Field Id.
    /// </value>
    get_HiddenFieldId : function() {
        return this._hiddenFieldId;
    },
    set_HiddenFieldId : function(value) {
        this._hiddenFieldId = value;
    },
    
    /// <value type="String">
    /// Get or Set the List Label
    /// </value>
    get_ListLabel : function() {
        return this._listLabel;
    },
    set_ListLabel : function(value) {
        this._listLabel = value;
    },
    
    /// <value type="Int">
    /// Get or Set the Asterisk Position
    /// </value>
    get_AsteriskElementTarget : function() {
        return this._asteriskElementTarget;
    },
    set_AsteriskElementTarget : function(value) {
        this._asteriskElementTarget = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_SearchButtonText : function() {
        return this._searchButtonText;
    },
    set_SearchButtonText : function(value) {
        this._searchButtonText = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_RowsToDisplay : function() {
        return this._rowsToDisplay;
    },
    set_RowsToDisplay : function(value) {
        this._rowsToDisplay = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_InternalValidationGroup : function() {
        return this._internalValidationGroup;
    },
    set_InternalValidationGroup : function(value) {
        this._internalValidationGroup = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_TextBoxId : function() {
        return this._textBoxId;
    },
    set_TextBoxId : function(value) {
        this._textBoxId = value;
    },
    
    /// <value type="String">
    /// Get or set hidden selection field id.
    /// </value>
    get_SelectionVisibleFieldId : function() {
        return this._selectionVisibleFieldId;
    },
    set_SelectionVisibleFieldId : function(value) {
        this._selectionVisibleFieldId = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_DisableEntry : function() {
        return this._disableEntry;
    },
    set_DisableEntry : function(value) {
        this._disableEntry = value;
    },
    
    /// <value type="Boolean">
    /// Get or set the required field.
    /// </value>
    get_Required : function() {
        return this._required;
    },
    set_Required : function(value) {
        this._required = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_DisableSearch : function() {
        return this._disableSearch;
    },
    set_DisableSearch : function(value) {
        this._disableSearch = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_Text : function() {
        return this.get_ClientState().Text;
    },
    set_Text : function(value) {
        this.get_ClientState().Text = value;
        this.SaveClientStateField();
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_RequiredFieldId : function() {
        return this._requiredFieldId;
    },
    set_RequiredFieldId : function(value) {
        this._requiredFieldId = value;
    },
	
	/// <value type="String">
    /// Selected value of the drop down
    /// </value>
    get_Value : function() {
        return this.get_ClientState().Value;
    },
    set_Value : function(value, text) {
        this.get_ClientState().Value = value;
        this.SaveClientStateField();
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ServiceMethod : function() {
        return this._serviceMethod;
    },
    set_ServiceMethod : function(value) {
        this._serviceMethod = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ServicePath : function() {
        return this._servicePath;
    },
    set_ServicePath : function(value) {
        this._servicePath = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ListPromptText : function() {
        return this._listPromptText;
    },
    set_ListPromptText : function(value) {
        this._listPromptText = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_OnPreQuery : function() {
        return this._onPreQuery;
    },
    set_OnPreQuery : function(value) {
        this._onPreQuery = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_OnPostQuery : function() {
        return this._onPostQuery;
    },
    set_OnPostQuery : function(value) {
        this._onPostQuery = value;
    },
    
     /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ContainerId : function() {
        return this._containerId;
    },
    set_ContainerId : function(value) {
        this._containerId = value;
    }
}

Controls.LookupBehavior.registerClass('Controls.LookupBehavior', Controls.ScriptControl);

/************************************************************************************************/
/* Base Panel Tooltip Behaviour                                                                 */
/************************************************************************************************/

Controls.BaseTooltipPanelBehaviour = function(element)
{
    Controls.BaseTooltipPanelBehaviour.initializeBase(this, [element]);
    this._controlToTarget;
    this._offsetX;
    this._offsetY;
    this._displayType;
    this._tooltip;
    this._element;
    this._ieFrame;
    this._XYSource;
    this._tooltipInactiveCssClass;   
    this._tooltipActiveCssClass;
    this._tooltipFinalOffsetX;
    this._tooltipFinalOffsetY;
}

Controls.BaseTooltipPanelBehaviour.prototype = {

    /// <summary>
    /// Initialise Function
    /// </summary>
    initialize : function() {

        Controls.BaseTooltipPanelBehaviour.callBaseMethod(this, 'initialize');  
        
        this._element = $get(this.get_ControlToTarget());
        this._tooltip = $get(this.get_Tooltip());
        
        //remove alt and title attributes so alt text does not float over the tooltip
        this._element.removeAttribute('alt');
        this._element.removeAttribute('title');

        this._tooltip.style.display = 'none';
        this._tooltip.style.zIndex = '1000';
        
        this._tooltipFinalOffsetX = 0;
        this._tooltipFinalOffsetY = 0;
                
        if(this._displayType.toLowerCase() == 'floating') {
            this._tooltip.style.position = 'absolute';
        }            

        //register tooltip...
        Controls.BaseTooltipPanelBehaviour.registerWindow(this);    
        
        var browser = navigator.userAgent.toLowerCase();
        this._element.IE = (browser.indexOf('msie') != -1);

	    if(this._element.IE) {
			// IE, what a browser..
			this._createIEFrame();
		}
    },
   
    /// <summary>
    /// Dispose Function
    /// </summary>
    dispose : function() {
        Controls.BaseTooltipPanelBehaviour.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    // create the IFRAME that prevents windowed controls, such as 'select' and 'object'
	// from showing through the suggestion list in IE
	// see [http://weblogs.asp.net/bleroy/archive/2005/08/09/how-to-put-a-div-over-a-select-in-ie.aspx]
	/// <summary>
    _createIEFrame : function() {
        this._ieframe = document.createElement('iframe');
		var frameStyle = this._ieframe.style;
		this._ieframe.setAttribute("scrolling", "no");
		this._ieframe.setAttribute("frameborder", "0");
		this._ieframe.setAttribute("src", "about:blank");   // its only ever in IE
		frameStyle.position = "absolute";
		frameStyle.border = "none";
		frameStyle.display = "none";
		frameStyle.zIndex = 998;
		this._element.parentNode.appendChild(this._ieframe);
		
    },
    
    /// <summary>
    /// Default member to get the co ordinates of the event occurence
    /// </summary>
    _getXandYCoOrdinatesOfEvent : function(e) {
    
        var x = Event.pointerX(e.rawEvent);
        var y = Event.pointerY(e.rawEvent); 
        return {X: x, Y: y};
    },
    
    /// <summary>
    /// Init, position and display IFrame
    /// <summary>
    _renderIFrame : function(dimensions, position) {

            this._ieframe.style.left = position.Left;
			this._ieframe.style.top = position.Top;
			this._ieframe.style.height =(dimensions.height) + 'px';
			this._ieframe.style.width =(dimensions.width) + 'px';
			this._ieframe.style.display='block';	
    },
    
    /// <summary>
    /// Calculates where to position the tooltip
    /// <summary>
    _calculateTooltipPosition : function(element, dimensions) {

        var left, top;
        var x = this._XYSource.X; //_XYSource.X
        var y = this._XYSource.Y; //_XYSource.Y   
        var viewportBoundries = this._getViewportBoundries();    
        
        var yTop = y - this._offsetY - dimensions.height;
        var yBottom = y + this._offsetY + dimensions.height;
        var yTopVisible = yTop - viewportBoundries.top;
        var yBottomVisible = viewportBoundries.bottom - yBottom;
        
        if(yBottomVisible >= 0) {
            top = (y + this._offsetY + this._tooltipFinalOffsetY) + 'px';
        }
        else {
            if(yTopVisible > yBottomVisible) {
                top = yTop + 'px';
            }
            else {
                top = (y + this._offsetY) + 'px';
            }
        }
        
        var xLeft = x + this._offsetX - dimensions.width;
        var xRight = x - this._offsetX + dimensions.width;
        var xLeftVisible = xLeft - viewportBoundries.left;
        var xRightVisible = viewportBoundries.right - xRight;
 
        if(xRightVisible >= 0) {
            left = (x - this._offsetX + this._tooltipFinalOffsetX) + 'px';
        }
        else {
            if(xLeftVisible > xRightVisible) {
                left = xLeft + 'px';
            }
            else {
                left = (x - this._offsetX) + 'px';
            }
        }
        return {Left: left, Top: top};
    },
    
    
    /// <summary>
    /// Base member to get the abosolute position of an element on the page
    /// <summary>
    _truePosition : function(element) {
    
        var pos = cumulativeOffset(element);
        if(window.opera) { return pos; }
        var iebody   = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
        var dsocleft = document.all ? iebody.scrollLeft : window.pageXOffset;
        var dsoctop  = document.all ? iebody.scrollTop  : window.pageYOffset;
        var posReal  = realOffset(element);
	        
	    var y =  pos[1] - posReal[1] + dsoctop;
	    var x = pos[0] - posReal[0] + dsocleft;
	
	    return {X: x, Y: y};
	},
	
	/// <summary>
	/// Helper member for _truePosition to get the abosolute position of an element on the page
	/// <summary>
   _realOffset : function(element) {
   
	    var t = 0, l = 0;
        do {
                t += element.scrollTop  || 0;
                l += element.scrollLeft || 0;
                element = element.parentNode;
        } while (element);
        return [l, t];
	},
	
    /// <summary>
	/// Helper member for _truePosition to get the abosolute position of an element on the page
	/// <summary>
	_cumulativeOffset : function(element) {
	
	  var t = 0, l = 0;
        
        do {
			if (isRelativeElement(element)) {
				// Relative positioned element without actual positioning - the browsers tend to incorrectly define this element as an offset parent.
			} else {
				t += element.offsetTop  || 0;
				l += element.offsetLeft || 0;
			}
			element = element.offsetParent;
        } while (element);
        return [l, t];
	},	
    
    /// <summary>
	/// Hides the tooltip
	/// <summary>
    _hideTooltip : function() {
        
        if(this._element != null && this._element.src != null)
	        this._element.src = this._element.src.replace(/_pressed/,'');
	                
        if(this._tooltipInactiveCssClass !=  null) {
	        this._tooltip.className = this._tooltipInactiveCssClass;	    
	    }
	    
	    if(this._ieframe != null) {
	        this._ieframe.style.display = "none";
	    }
	    
	    this._tooltip.style.display = 'none';								
    },
    
    /// <summary>
	/// Shows the tooltip
	/// <summary>
    _showTooltip : function() {
        
        if(this._displayType.toLowerCase() == 'floating') {
            var dimensions = Element.getDimensions(this._tooltip);
            var CoOrdsToPositionTooltip = this._calculateTooltipPosition(this._tooltip, dimensions);
            
            this._tooltip.style.left = CoOrdsToPositionTooltip.Left;
            this._tooltip.style.top = CoOrdsToPositionTooltip.Top;
            
            if(this._ieframe != null) {
                    this._renderIFrame(dimensions, CoOrdsToPositionTooltip);
            }
        }
        
        this._tooltip.style.display = 'block';
    },
    
    /// <value type="String">
    /// Gets the viewport size.
    /// </value>
    _getViewportBoundries: function() {
    
        var t = 0;
        var l = 0; 
        var b = 0; 
        var r = 0;

        //IE
        if(!window.innerWidth) {
	        //strict mode
	        if(!(document.documentElement.clientWidth == 0)) {
	            t = document.documentElement.scrollTop;
	            l = document.documentElement.scrollLeft;
	            b = document.documentElement.clientHeight + document.documentElement.scrollTop;
	            r = document.documentElement.clientWidth + document.documentElement.scrollLeft;
	        }
	        //quirks mode
	        else {
	            t = document.body.scrollTop;
	            l = document.body.scrollLeft;
	            b = document.body.clientHeight + document.body.scrollTop;
	            r = document.body.clientWidth + document.body.scrollLeft;
	        }
        }
        //w3c
        else {
            t = window.pageYOffset;
            l = window.pageXOffset;
            b = window.innerHeight + window.pageYOffset;
            r = window.innerWidth + window.pageXOffset;
        }
        
        return {top: t, right: r, bottom: b, left: l};
    },
        
    /// <value type="String">
    /// Gets or sets the id of the control to target.
    /// </value>
    get_ControlToTarget : function() {
        return this._controlToTarget;
    },
    set_ControlToTarget : function(value) {
        this._controlToTarget = value;
    }, 
    
    /// <value type="String">
    /// Gets or sets the X co-ordinate offset.
    /// </value>
    get_OffsetX : function() {
        return this._offsetX;
    },
    set_OffsetX : function(value) {
        this._offsetX = value;
    }, 
    
    /// <value type="String">
    /// Gets or sets the Y co-ordinate offset.
    /// </value>
    get_OffsetY : function() {
        return this._offsetY;
    },
    set_OffsetY : function(value) {
        this._offsetY = value;
    }, 
    
    /// <value type="String">
    /// Gets or sets the tooltip display type.
    /// </value>
    get_DisplayType : function() {
        return this._displayType;
    },
    set_DisplayType : function(value) {
        this._displayType = value;
    }, 
    
    /// <value type="String">
    /// Gets or sets the tooltip display type.
    /// </value>
    get_Tooltip : function() {
        return this._tooltip;
    },
    set_Tooltip : function(value) {
        this._tooltip = value;
    },
    
    /// <value type="String">
    /// Gets or sets the Inactive CssClass.
    /// </value>
    get_TooltipInactiveCssClass : function() {
        return this._tooltipInactiveCssClass;
    },
    set_TooltipInactiveCssClass : function(value) {
        this._tooltipInactiveCssClass = value;
    },
    
    /// <value type="String">
    /// Gets or sets the activeCssClass.
    /// </value>
    get_TooltipActiveCssClass : function() {
        return this._tooltipActiveCssClass;
    },
    set_TooltipActiveCssClass : function(value) {
        this._tooltipActiveCssClass = value;
    }
}

/************************************************************************************************/
/* Base Panel Tooltip Behaviour - Static Members and Properties                                 */
/************************************************************************************************/

/// <summary>
///Static Member documentWindows
/// <summary>
Controls.BaseTooltipPanelBehaviour.documentWindows = new Array();


/// <summary>
///Static Member documentWindowsCount
/// <summary>
Controls.BaseTooltipPanelBehaviour.documentWindowsCount = 0;

/// <summary>
/// Global show window static method
/// <summary>
Controls.BaseTooltipPanelBehaviour.showWindow = function(baseTooltipPanel){
    
    if(baseTooltipPanel._tooltipActiveCssClass != null) {
        baseTooltipPanel._tooltip.className= baseTooltipPanel._tooltipActiveCssClass;
    }
    
    baseTooltipPanel._showTooltip();
} 

/// <summary>
/// Registers the tooltip for tracking/hiding and showing
/// <summary>
Controls.BaseTooltipPanelBehaviour.registerWindow = function(baseTooltipPanel){

    Controls.BaseTooltipPanelBehaviour.documentWindows[Controls.BaseTooltipPanelBehaviour.documentWindowsCount] = baseTooltipPanel;
    Controls.BaseTooltipPanelBehaviour.documentWindowsCount++;
}

/// <summary>
/// Global show window static method
/// <summary>
Controls.BaseTooltipPanelBehaviour.hideAllWindows = function(baseTooltipPanel, elementId) {

    if(baseTooltipPanel != null) {
    
        baseTooltipPanel._hideTooltip();   
        
    }
    else if(elementId != null) {
    
       for(var index = 0; index < Controls.BaseTooltipPanelBehaviour.documentWindows.length; index++ ) {            
       
            baseTooltipPanel = Controls.BaseTooltipPanelBehaviour.documentWindows[index];
            
            var currentElementId = baseTooltipPanel.get_ControlToTarget();
             
            if(currentElementId == elementId) {
            
                baseTooltipPanel._hideTooltip();
                
            }  
        } 
    }
    else {   //Hide All Windows 
    
        for(var index = 0; index < Controls.BaseTooltipPanelBehaviour.documentWindows.length; index++ ) {
        
            Controls.BaseTooltipPanelBehaviour.documentWindows[index]._hideTooltip();  
            
        }
    }
}

Controls.BaseTooltipPanelBehaviour.registerClass('Controls.BaseTooltipPanelBehaviour', Controls.ScriptControl);

/************************************************************************************************/
/* Help Tooltip Behaviour                                                                       */
/************************************************************************************************/

Controls.HelpTooltipBehaviour = function(element)
{
    Controls.HelpTooltipBehaviour.initializeBase(this, [element]);
    this._targetControlClickHandler;
}

Controls.HelpTooltipBehaviour.prototype = {

    /// <summary>
    /// Initialise Function
    /// </summary>
    initialize : function() {
        Controls.HelpTooltipBehaviour.callBaseMethod(this, 'initialize');   
        
        this._tooltipFinalOffsetX = 20;
        this._tooltipFinalOffsetY = 5;
        
        this._targetControlClickHandler = Function.createDelegate(this, this._targetControlClick);
        $addHandler(this._element, "click", this._targetControlClickHandler)
    },
   
    /// <summary>
    /// Dispose Function
    /// </summary>
    dispose : function() {
        Controls.HelpTooltipBehaviour.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Gets the X Y co ords of the element where the event occurred
    /// </summary>
    _getXandYCoOrdinatesforElement : function() {    
        return this._truePosition(this._element);
    },
    
    /// <summary>
    /// Element Click Event Behaviour
    /// </summary>
    _targetControlClick : function(e) {
	
	this._XYSource = this._getXandYCoOrdinatesforElement();
	
	if(this._tooltip.style.display == 'none') {        
        Controls.BaseTooltipPanelBehaviour.showWindow(this);
        
        if(this._element != null && this._element.src != null) {
	        this._element.src = this._element.src.replace(/.png/,'_pressed.png');	
	    }
	        
        if((!this._element.IE) && (this._displayType.toLowerCase() == 'floating')) {
           new Draggable(this._tooltip,{startEffect:'none'});		
        }
	}
	else {    
        Controls.BaseTooltipPanelBehaviour.hideAllWindows(this);
        
        if(this._element != null && this._element.src != null)
	        this._element.src = this._element.src.replace(/_pressed/,'');
	}
  }
}

Controls.HelpTooltipBehaviour.registerClass('Controls.HelpTooltipBehaviour', Controls.BaseTooltipPanelBehaviour);

/************************************************************************************************/
/* Floating Tooltip Behaviour                                                                   */
/************************************************************************************************/

Controls.FloatingTooltipBehaviour = function(element)
{
    Controls.FloatingTooltipBehaviour.initializeBase(this, [element]);
    
    this._elementMouseOutHandler = null;
    this._elementMouseMoveHandler = null;
    this._tooltip;
    this._visible = false;    
}

Controls.FloatingTooltipBehaviour.prototype = {

    /// <summary>
    /// Initialise Function
    /// </summary>
    initialize : function() {
    
        Controls.FloatingTooltipBehaviour.callBaseMethod(this, 'initialize');  
        
        // Set style on the tool tip.
        this._tooltip.style.position = 'absolute';
        this._tooltip.style.padding = '0px';
        this._tooltip.style.margin = '0px';
      
        this._elementMouseOutHandler = Function.createDelegate(this, this._elementMouseOut);
        $addHandler(this._element, "mouseout", this._elementMouseOutHandler)     
        
        this._elementMouseMoveHandler = Function.createDelegate(this, this._elementMouseMove);
        $addHandler(this._element, "mousemove", this._elementMouseMoveHandler);    
    },
   
    /// <summary>
    /// Dispose Function
    /// </summary>
    dispose : function() {
        Controls.FloatingTooltipBehaviour.callBaseMethod(this, 'dispose');
    },
    
    /// <summary>
    /// Mouse out event handler
    /// </summary>
    _elementMouseOut : function(e) {
            Controls.BaseTooltipPanelBehaviour.hideAllWindows(this);
    },
    
    /// <summary>
    /// Mouse move event handler
    /// </summary>
    _elementMouseMove : function(e) {
        this._XYSource = this._getXandYCoOrdinatesOfEvent(e);
        Controls.BaseTooltipPanelBehaviour.showWindow(this);
    }
}

Controls.FloatingTooltipBehaviour.registerClass('Controls.FloatingTooltipBehaviour', Controls.BaseTooltipPanelBehaviour);

/************************************************************************************************/
/* LookupBSite Behavior                                                                              */
/************************************************************************************************/

Controls.LookupBSiteBehavior = function(element)
{
    Controls.LookupBSiteBehavior.initializeBase(this, [element]); 
    this._hiddenFieldId = '';
    this._requiredFieldId = '';
    this._textBoxId = '';
    this._containerId = '';
    this._listLabel = '';
    this._asteriskElementTarget = '';
    this._searchButtonText = '';
    this._searchButtonImage = '';
    this._searchButtonImageDisabled = '';
    this._button;
    this._rowsToDisplay = '';
    this._internalValidationGroup = '';
    this._servicePath = '';
    this._serviceMethod = '';
    this._div = null;
    this._text = '';
    this._buttonClickHandler;
    this._itemClickHandler;
    this._disableEntry = false;
    this._disableSearch = false;
    this._listPromptText = '';
    this._onPreQuery = '';
    this._onPostQuery = '';
}

Controls.LookupBSiteBehavior.prototype = {

    /// <summary>
    /// Initialise Function.
    /// </summary>
    initialize : function() {
        Controls.LookupBSiteBehavior.callBaseMethod(this, 'initialize');      
        this._setupControl();  
    },
    
    /// <summary>
    /// Dispose Function.
    /// </summary>
    dispose : function() {
        Controls.LookupBSiteBehavior.callBaseMethod(this, 'dispose');
        
        if (this._fieldFocusHandler != null) {
            $removeHandler(this.getTextField(), "focus", this._fieldFocusHandler);
            this._fieldFocusHandler = null;
        }
    },
    
    /// <summary>
    /// Sets the value on the hidden field.
    /// </summary>
    /// <returns />
    _setHiddenField : function(value) {
        var hiddenField = $get(this._hiddenFieldId);
        hiddenField.value = value;
    },
    
    /// <summary>
    /// Sets the value on the hidden field.
    /// </summary>
    _setRequiredField : function(value) {
        var requiredField = $get(this._requiredFieldId);
        requiredField.value = value;
    },
    
    /// <summary>
    /// Triggers the on pre query event.
    /// </summary>
    /// <return>Parameter Object</returns>
    _triggerOnPreQueryEvent : function() {
        var paramobject = null;
        if(this._onPreQuery != null && this._onPreQuery.length > 0) {
            var paramobject = eval(this._onPreQuery + '()');
        }
        return paramobject;
    },
    
    /// Triggers the on post query event.
    /// </summary>
    _triggerOnPostQueryEvent : function(value) {
        if(this._onPostQuery != null && this._onPostQuery.length > 0) {
            eval(this._onPostQuery + '({"failed":' + value + '})');
        }
    },
    
    /// <summary>
    /// Valdiates this controls child controls.
    /// </summary>
    /// <returns>true if valid ; else false.</return>
    _validateControl : function() {
        var validationResult = true;
        
        if (typeof(Page_ClientValidate) == 'function') {
            validationResult = Page_ClientValidate(this._internalValidationGroup);
            Page_IsValid = true;
            Page_BlockSubmit = false;
        }
        return validationResult;
    },
    
    getTextField: function() {
        if (this._textField == null)
            this._textField = $get(this.get_TextBoxId());
        return this._textField;
    },
    
    /// <summary>
    /// Sets up the control.
    /// </summary>
    _setupControl : function() {
        var container = $get(this._containerId);

        if (this._disableEntry) {
            textbox.setAttribute("disabled", "true");
        }
        
        if (!this._disableSearch) {
            var br = Msfg$createElement('br');
            container.appendChild(br);
            
		    this._button = Msfg$createElement('input');
		    
		    if (this.get_Text() != null && this.get_Text().length > 0 )
            {
                this._setButtonEnabled(true);
            }
            else
            {
                this._setButtonEnabled(false);   
            }
		    
            this._button.setAttribute("type","image");
            this._button.setAttribute("id", this._containerId + "_btnSubmit");
            this._button.setAttribute("alt", this.get_SearchButtonText());
            this._button.setAttribute("title", this.get_SearchButtonText());
            Element.addClassName(this._button, "msfg-lookup-button");
            container.appendChild(this._button);
            
            this._fieldFocusHandler = Function.createDelegate(this, this._fieldOnFocus);
            $addHandler(this.getTextField(), "focus", this._fieldFocusHandler);
            
            this._buttonClickHandler = Function.createDelegate(this, this._buttonClick);
		    $addHandler(this._button, "click", this._buttonClickHandler);
		}
		
        if (this.get_Text() != null && this.get_Text().length > 0 ) {
            $get(this.get_TextBoxId()).value = this.get_Text();
            this._createWaitingIcon();
            this._getData(this.get_Text());
        }
    },
    
    /// <summary>
    /// Called in response to the search
    /// button being clicked.
    /// </summary>
    _buttonClick : function(sender, args) {
        this._destroyList();
        this._setHiddenField("false");
        this._setRequiredField("false");
        this.set_Value("");
        
        var validationResult = this._validateControl();
        if(validationResult) {          
            this._createWaitingIcon();
            var value = $get(this.get_TextBoxId()).value;
            this.set_Text(value);
            this._getData(value);
        }
        
        
        if (typeof sender.rawEvent.stopPropagation == "function")
        {
            sender.rawEvent.stopPropagation();
            sender.rawEvent.preventDefault();
        }
        else
        {
            sender.rawEvent.cancelBubble = true;
        }
            
        return false;
    },
    
    /// <summary>
    /// Called in response focus being set on text box
    /// </summary>
    _fieldOnFocus: function(sender, args) {
        this._setButtonEnabled(true);
    },
    
    /// <summary>
    /// Sets the required state of the button (disabled or enabled)
    /// </summary>
    _setButtonEnabled: function(requiredState) {
 
        if (requiredState == true) {
            this._button.removeAttribute("disabled");
            this._button.setAttribute("style", "cursor:pointer;");
            this._button.src = this._searchButtonImage;
        } else {
            this._button.setAttribute("disabled", "true");
            this._button.setAttribute("style", "cursor:default;");
            this._button.src = this._searchButtonImageDisabled;
            
        }
    },
    
    /// <summary>
    /// Called in response to a item being
    /// selected form the results list.
    /// </summary>
    _itemSelected : function(e) {
        if(e.target.value == "")
        {
            this.set_Value("");
            this._setRequiredField("false");
        }
        else
        {
            this.set_Value(e.target.value);
            this._setRequiredField("true");
        }
    },
    
    /// <summary>
    /// Create the results list.
    /// </summary>
    _createList : function(list) {
        var container = this.get_element();
        var div = Msfg$createElement('div');
        div.setAttribute("id", this._containerId + "_divSelectContainer");
        Element.addClassName(div, "msfg-lookup-list");
        container.appendChild(div);
        this._div = div;
        
        var label = Msfg$createElement('label');
        label.setAttribute("id", this._containerId + "_lblSelect");
        this._button.setAttribute("value", this._searchButtonText);
        label.appendChild(document.createTextNode(this._listLabel + ' '));
        div.appendChild(label);
        
        var asterisk = Msfg$createElement('em');
        asterisk.appendChild(document.createTextNode('*'));
            
        if(this._asteriskElementTarget == 1) {
            label.appendChild(asterisk);
        }
        
        var select = Msfg$createElement('select');
        select.setAttribute("id", this._containerId + "_cboSelect");
        select.setAttribute("size",this._rowsToDisplay);
        
        if (list) {
            var selectedIndex = 0;
            
            var defaultOptionElement = new Option(this.get_ListPromptText(), "", false, false);
            select.options[select.options.length] = defaultOptionElement;
            
            for (i = 0 ; i < list.length ; i++) {
                var listItemName = list[i].Name;
                var listItemValue = list[i].Value;
                
                var selected = false;
                
                if(listItemValue == this.get_Value()) {
                    selectedIndex = i + 1;
                }
               
                var optionElement = new Option(listItemName, listItemValue, false, selected);
                select.options[select.options.length] = optionElement;
            }
            
            if(list.length == 1) {
                selectedIndex = 1;
                this.set_Value(listItemValue);
                
            }
            
            if(selectedIndex > 0) {
                this._setRequiredField("true");
            }
            select.selectedIndex = selectedIndex;
        }
      
        div.appendChild(select);
        
        if(this._asteriskElementTarget != 1) {
            div.appendChild(asterisk);
        }
        
        this._itemClickHandler = Function.createDelegate(this, this._itemSelected);
		$addHandler(select, "change", this._itemClickHandler);
    },
        
    /// <summary>
    /// Destroy the results list.
    /// </summary>
    _destroyList : function(){ 
        if(this._div != null) {
            e = this.get_element();
            e.removeChild(this._div);
            this._div = null;
        }
    },
    
    /// <summary>
    /// Create the waiting icon panel.
    /// </summary>
    _createWaitingIcon : function(){ 
        var container = this.get_element();
        var div = Msfg$createElement('div');
        Element.addClassName(div, "msfg-lookup-progresspanel");
        div.appendChild(document.createTextNode("Loading please wait..."));
        container.appendChild(div);
        this._div = div;
    },
    
    /// <summary>
    /// Destory the waiting icon panel.
    /// </summary>
    _destroyWaitingIcon : function(){ 
        if(this._div != null) {
            e = this.get_element();
            e.removeChild(this._div);
            this._div = null;
        }
    },
    
    /// <summary>
    /// Called to get the data from the web service.
    /// </summary>
    _getData : function(text) {
        if (this._servicePath && this._serviceMethod) {

            var paramObject = this._triggerOnPreQueryEvent();

            Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false,
                { value:text, parameters:paramObject},
                Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError));
        }
    },
    
    /// <summary>
    /// Called when the web service compeltes it's call
    /// </summary>
    /// <returns />
    _onMethodComplete : function(result, userContext, methodName) {
        this._destroyWaitingIcon();
        if(result.length > 0 ) {
            this._triggerOnPostQueryEvent(false);
            this._createList(result);
        } else {
            this._triggerOnPostQueryEvent(true);
            this._setHiddenField("true");
            var validationResult = this._validateControl();
        }
    },

    /// <summary>
    /// Called when the web service faileds it's call
    /// </summary>
    /// <returns />
    _onMethodError : function(webServiceError, userContext, methodName) {
        this._triggerOnPostQueryEvent(true);
        this._destroyWaitingIcon();
        this._setHiddenField("true");
        var validationResult = this._validateControl();
    },
        
    /// <value type="String">
    /// Get or Set Hidden Field Id.
    /// </value>
    get_HiddenFieldId : function() {
        return this._hiddenFieldId;
    },
    set_HiddenFieldId : function(value) {
        this._hiddenFieldId = value;
    },
    
    /// <value type="String">
    /// Get or Set the List Label
    /// </value>
    get_ListLabel : function() {
        return this._listLabel;
    },
    set_ListLabel : function(value) {
        this._listLabel = value;
    },
    
    /// <value type="Int">
    /// Get or Set the Asterisk Position
    /// </value>
    get_AsteriskElementTarget : function() {
        return this._asteriskElementTarget;
    },
    set_AsteriskElementTarget : function(value) {
        this._asteriskElementTarget = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_SearchButtonText : function() {
        return this._searchButtonText;
    },
    set_SearchButtonText : function(value) {
        this._searchButtonText = value;
    },
        
    /// <value type="String">
    /// Get or set Search Button Image Url.
    /// </value>
    get_SearchButtonImage : function() {
        return this._searchButtonImage;
    },
    set_SearchButtonImage : function(value) {
        this._searchButtonImage = value;
    },
        
    /// <value type="String">
    /// Get or set Search Button Image Disabled Url.
    /// </value>
    get_SearchButtonImageDisabled : function() {
        return this._searchButtonImageDisabled;
    },
    set_SearchButtonImageDisabled : function(value) {
        this._searchButtonImageDisabled = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_RowsToDisplay : function() {
        return this._rowsToDisplay;
    },
    set_RowsToDisplay : function(value) {
        this._rowsToDisplay = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_InternalValidationGroup : function() {
        return this._internalValidationGroup;
    },
    set_InternalValidationGroup : function(value) {
        this._internalValidationGroup = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_TextBoxId : function() {
        return this._textBoxId;
    },
    set_TextBoxId : function(value) {
        this._textBoxId = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_DisableEntry : function() {
        return this._disableEntry;
    },
    set_DisableEntry : function(value) {
        this._disableEntry = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_DisableSearch : function() {
        return this._disableSearch;
    },
    set_DisableSearch : function(value) {
        this._disableSearch = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_Text : function() {
        return this.get_ClientState().Text;
    },
    set_Text : function(value) {
        this.get_ClientState().Text = value;
        this.SaveClientStateField();
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_RequiredFieldId : function() {
        return this._requiredFieldId;
    },
    set_RequiredFieldId : function(value) {
        this._requiredFieldId = value;
    },
	
	/// <value type="String">
    /// Selected value of the drop down
    /// </value>
    get_Value : function() {
        return this.get_ClientState().Value;
    },
    set_Value : function(value, text) {
        this.get_ClientState().Value = value;
        this.SaveClientStateField();
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ServiceMethod : function() {
        return this._serviceMethod;
    },
    set_ServiceMethod : function(value) {
        this._serviceMethod = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ServicePath : function() {
        return this._servicePath;
    },
    set_ServicePath : function(value) {
        this._servicePath = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ListPromptText : function() {
        return this._listPromptText;
    },
    set_ListPromptText : function(value) {
        this._listPromptText = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_OnPreQuery : function() {
        return this._onPreQuery;
    },
    set_OnPreQuery : function(value) {
        this._onPreQuery = value;
    },
    
    /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_OnPostQuery : function() {
        return this._onPostQuery;
    },
    set_OnPostQuery : function(value) {
        this._onPostQuery = value;
    },
    
     /// <value type="String">
    /// Get or set Search Button Text.
    /// </value>
    get_ContainerId : function() {
        return this._containerId;
    },
    set_ContainerId : function(value) {
        this._containerId = value;
    }
}

Controls.LookupBSiteBehavior.registerClass('Controls.LookupBSiteBehavior', Controls.ScriptControl);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();