﻿
Type.registerNamespace('DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch');

/// <summary>
/// LocationReturnSearch
///    Extender that populates DOM element with return locations based on the pickup location value.
///     The text entered on the return target text box is used to filter this list of return locations.
///     After the user has selected a Pick-up Location and begins typing in the Return Location field, 
///     one of five results can occur. 
///     1)Many locations available. If more than 10 Return locations are available, then Predictive Typing
///         begins. The control matches the letters typed against the available list, and presents a subset
///         of matches. 
///     2)Few locations available. If fewer than 10 Return locations are available, then all locations are immediately 
///         presented no matter what the user has typed.
///     3)No match. There may be available locations, but none match what the user has typed. If no locations 
///         are available, an error message is presented. 
///     4)No locations available. Some locations, such as Alaska or Hawaii, do not allow one-way rentals. In 
///         this case, the control would respond with an error message.
///     5)If the user begins typing in the Return field before selecting a Pick-up location, an error 
///         occurs.
///      The location images are formated using the sitecore image library to set image source. 
/// </summary>
/// <remarks>
///    properties: 
///      {name: 'TargetControlID', type: String} : Default value for extenders identifing the TextBox control
///      {name: 'MinimumPrefixLength', type: Number} : number of chars in which to start calling webservice for results
///      {name: 'ItemsPerPage', type: Number} : Number of location items to display before Predictive typing starts
///      {name: 'PickupLocationCodeId', type: String} : id of element where the pickup location is populated
///      {name: 'ReturnLocationCodeId', type: String} : id of element that is populated with return location code when a return location is selected
///      {name: 'ManyLocationsText', type: String} : Text to display for 1)	Many locations available. 
///      {name: 'FewLocationsText', type: String} : Text to display for 2) Few locations available.
///      {name: 'NoMatchText', type: String} : Text to display for 3) No match.
///      {name: 'NoMatchLinkText', type: String} : Text to display for 3) No match for link.
///      {name: 'NoLocationFoundText', type: String} : Text to display for 4) No locations available.
///      {name: 'NoPickupLocationText', type: String} : Text to display for 5) display No Pickup Location
///      {name: 'HighlightItemBgColor', type: String} : background color for list item highlight
///      {name: 'DefaultItemBgColor', type: String} : default background color for list item
///      {name: 'WebServiceErrorText', type: String} : ID to the Sitecore error.  Call the Validator to retrieve the error
///                 and call the Validator to render the error message. The render includes html tags 
///      {name: 'WatermarkBehaviorId', type: String} : id of behavior extender that identifies the
///                 AjaxControlToolkit watermark extender
/// </remarks>
/// <history>
///     <change date="01/16/2007 11:48:00" ticket="">
///         <author>Steven Berenbrock</author>
///         <description>Initial version.</description>
///     </change>
///     <change date="12/12/2007">
///         <author>Jscott1 - SiteDynamics</author>
///         <description>Added new property: FilterB2B.  This will filter the locations for use in B2B</description>
///     </change>
/// </history>

DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior = function(element) {
    DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior.initializeBase(this, [element]);
    ///<summary>LocationSearchBehavior Extender</summary>
    ///<param name="element">Associated element</param>

    //Properties
    this._minimumPrefixLength = 3;
    this._itemPredictiveThreshold = 10;
    this._pickupLocationCodeId = null;
    this._returnLocationCodeId = null;
    this._manyLocationsText = null;
    this._fewLocationsText = null;
    this._noMatchText = null;
    this._noMatchLinkText = null;
    this._noLocationFoundText = null;
    this._noPickupLocationText = null;
    this._highlightItemBgColor = null;
    this._defaultItemBgColor = "#ffffff";
    this._webServiceErrorText = null;
    this._watermarkBehaviorId = null;
    this._filterB2B = false;

    // Member variables
    this._returnContainer = null;
    this._messageElement = null;
    this._listElement = null;
    this._selectIndex = -1;
    this._popupBehavior = null;
    this._pickupLocationCode = null;
    this._lastPickupLocationCode = null;
    this._returnLocations = null;
    this._allRtnLocations = null;
    this._showAllLocations = false;
    this._timer = null;
    this._completionInterval = 1000;
    this._currentPrefix = null;

    // event delegates
    this._tickHandler = null;
    this._bodyClickHandler = null;
    this._element$delegates = {
        blur : Function.createDelegate(this, this._onLostFocus),
        focus : Function.createDelegate(this, this._onGotFocus),
        keydown : Function.createDelegate(this, this._onKeyDown),
        keypress : Function.createDelegate(this, this._onKeyPress)
    };
    this._list$delegates = { 
        mousedown : Function.createDelegate(this, this._onListMouseDown),
        mouseover : Function.createDelegate(this, this._onListMouseOver),
        keydown : Function.createDelegate(this, this._onKeyDown),
        keypress : Function.createDelegate(this, this._onKeyPress)
    };
    
    // "Constants"
    this._itemCssName = 'Item';
    this._ieItemCssName = 'IeItem';
    this._descriptionCssName = 'Description';
    this._messageCssName = 'Message';
    this._messageBoxCssName = 'MessageBox';
    this._listCssName = 'List';
    
    this._itemIdentifier = '_LocationReturn_item_';
    this._itemLinkIdentifier =  '_link106';
    this._listElementIdSuffix = "_LocationReturn_list"
    this._messageElementIdSuffix = "_LocationReturn_msg"
    this._returnElementIdSuffix = "_LocationReturn"
    
    this._metroName = "Metro";
    this._countryName = "Country";
    this._stateName = "State";
}

DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior.prototype = {
    //*****************************************************************
    //Override methods
    //
    initialize : function() {
        DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior.callBaseMethod(this, 'initialize');
        // init Timer delegate vars
        this._createTimer();

        //init target delegate vars
        var targetElement = this.get_element();
        targetElement.setAttribute("autocomplete", "off");
        $addHandlers(targetElement, this._element$delegates);

        //create return container element
        if (!this._returnContainer) {
            this._returnContainer = document.createElement('div');
            this._returnContainer.id = targetElement.id + this._returnElementIdSuffix;
            this._returnContainer.className = "LocationReturnSearchControl";
       	    this._returnContainer.style.visibility = "hidden";
            document.body.appendChild(this._returnContainer);
            
            // add pop extender behavior to the newly created div
            this._popupBehavior = $create(AjaxControlToolkit.PopupBehavior, { positioningMode : AjaxControlToolkit.PositioningMode.BottomLeft, parentElement : targetElement, y : -1 }, { }, null, this._returnContainer);
            Sys.Debug.assert(this._popupBehavior != null, "Couldn't create popup behavior for return control '" + this._returnContainer.id + "'");
        }

        //create message container element
        if (!this._messageElement) {
            this._messageElement = document.createElement('div');
            this._messageElement.id = targetElement.id + this._messageElementIdSuffix;
            this._messageElement.className = this._messageBoxCssName;
       	    this._messageElement.style.display= "none";
            this._returnContainer.appendChild(this._messageElement);
        }

        //create list container element
        if (!this._listElement) {
            this._listElement = document.createElement('div');
            this._listElement.id = targetElement.id + this._listElementIdSuffix;
            this._listElement.className = this._listCssName;
       	    this._listElement.style.display = "none";
            this._returnContainer.appendChild(this._listElement);
            
            //add event handlers to the list element    
            $addHandlers(this._listElement, this._list$delegates);
        }
    },

    dispose : function() {
        var targetElement = this.get_element();

        this._destroyTimer();

        if (this._popupBehavior) {
            this._popupBehavior.dispose();
            this._popupBehavior = null;
        }

        // Detach this element event handlers
        $common.removeHandlers(targetElement, this._element$delegates);

        // Detach body click event handlers
        if (this._bodyClickHandler) {
            $removeHandler(document.body, 'click', this._bodyClickHandler);
            this._bodyClickHandler = null;
        }
        
        // Detach list element event handlers
        if (this._listElement != null) {
            $common.removeHandlers(this._listElement, this._list$delegates);
            this._listElement = null;
        }
        
        DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior.callBaseMethod(this, 'dispose');
    },

    //*****************************************************************
    // Property get/set methods
    //
    get_MinimumPrefixLength : function() {
        return this._minimumPrefixLength;
    },
    
    set_MinimumPrefixLength : function(value) {
        this._minimumPrefixLength = value;
    },            

    get_ItemPredictiveThreshold : function() {
        return this._itemPredictiveThreshold;
    },
    
    set_ItemPredictiveThreshold : function(value) {
        this._itemPredictiveThreshold = value;
    },
    
    get_PickupLocationCodeId : function() {
        return this._pickupLocationCodeId;
    },
    
    set_PickupLocationCodeId : function(value) {
        this._pickupLocationCodeId = value;
    },
        
    get_ReturnLocationCodeId : function() {
        return this._returnLocationCodeId;
    },
    
    set_ReturnLocationCodeId : function(value) {
        this._returnLocationCodeId = value;
    },
            
    set_ManyLocationsText : function(value) {
        this._manyLocationsText = value;
    },
    
    get_ManyLocationsText : function() {
        return this._noMatchLinkText;
    },
 
    get_FewLocationsText : function() {
        return this._fewLocationsText;
    },
    
    set_FewLocationsText : function(value) {
        this._fewLocationsText = value;
    },
    
    get_NoMatchText : function() {
        return this._noMatchText;
    },
    
    set_NoMatchText : function(value) {
        this._noMatchText = value;
    },    

    set_NoMatchLinkText : function(value) {
        this._noMatchLinkText = value;
    },
    
    get_NoMatchLinkText : function() {
        return this._manyLocationsText;
    },
    
    get_NoLocationFoundText : function() {
        return this._noLocationFoundText;
    },
    
    set_NoLocationFoundText : function(value) {
        this._noLocationFoundText = value;
    },
    
    get_NoPickupLocationText : function() {
        return this._noPickupLocationText;
    },
    
    set_NoPickupLocationText : function(value) {
        this._noPickupLocationText = value;
    },

    set_HighlightItemBgColor : function(value) {
        this._highlightItemBgColor = value;
    },
    
    get_HighlightItemBgColor : function() {
        return this._highlightItemBgColor;
    },
    
    set_DefaultItemBgColor : function(value) {
        this._defaultItemBgColor = value;
    },
    
    get_DefaultItemBgColor : function() {
        return this._defaultItemBgColor;
    },        
    
    set_WebServiceErrorText : function(value) {
        this._webServiceErrorText = value;
    },
    
    get_WebServiceErrorText : function() {
        return this._webServiceErrorText;
    },

    set_WatermarkBehaviorId : function(value) {
        this._watermarkBehaviorId = value;
    },
    
    get_WatermarkBehaviorId : function() {
        return this._watermarkBehaviorId;
    },
    
    set_FilterB2B : function(value) {
        this._filterB2B = value;
    },
    
    get_FilterB2B : function() {  
        return this._filterB2B;
    },

    //*****************************************************************
    // Custom methods
    //
    _createTimer : function() {
        if (!this._timer) {
            if (!this._tickHandler) {
                this._tickHandler = Function.createDelegate(this, this._onTimerTick);
            }
            this._timer = new Sys.Timer();
            this._timer.set_interval(this._completionInterval);
            this._timer.add_tick(this._tickHandler);
        }
    },
    
    _destroyTimer : function() {
        if (this._timer) {
            this._timer.remove_tick(this._tickHandler);
            this._timer.set_enabled(false);
            this._timer.dispose();
            this._timer = null;
        }
    },    
    
    _timerOff : function() {
        if (this._timer) {
            this._timer.set_enabled(false);
        }
    },
    
    _timerOn : function() {
        if (this._timer) {
            this._timer.set_enabled(true);
        }
    },    
            
    _show : function(showMsg, showList) {
        if (this._popupBehavior) {
            showMsg ? this._showMessage() : this._hideMessage();
            showList ? this._showList() : this._hideList();
            this._popupBehavior.show();
        }        
    },
    
    _showList : function() {
        if (this._listElement) {
            this._listElement.style.display = "block";
        }
    },

    _showMessage : function() {
        if (this._messageElement) {
            this._messageElement.style.display = "block";
        }
    },

    _hide : function() {
        if (this._popupBehavior) {
            this._popupBehavior.hide();      
        }        
    },    
 
    _hideList : function() {
        if (this._listElement) {
            this._listElement.style.display = "none";
        }
    },

    _hideMessage : function() {
        if (this._messageElement) {
            this._messageElement.style.display = "none";
        }
    },
    
    _reset : function() {
        this._resetMessage();
        this._resetList();
    },
    
    _resetList : function() {
        if (this._listElement) {
            this._listElement.innerHTML = '';
        }
        this._selectIndex = -1;
    }, 
    
    _resetMessage : function() {
        if (this._messageElement) {
            this._messageElement.innerHTML = '';
        }
    },     

    _getText : function() {
        ///<summary>Public method _getText - gets the textbox value.  if the watermark behavior exists
        /// the value is retreieved from the behavior else it is retrieve from the text box</summary>
        ///<remarks> watermark behavior is checked so that the water mark text does interfere with the
        ///     processing of the return locations.  The watermark behavior extender checks if 
        ///     watermark is on returning empty string if true else returns value in text box</remarks>
        if (this._watermarkBehaviorId) {
            //find the extender behavior object
            var watermarkBehavior = $find(this._watermarkBehaviorId);
            if (watermarkBehavior) {
                return watermarkBehavior.get_Text();
            }
            else {
                return this.get_element().value;
            }
        }
        else {
            return this.get_element().value;
        }
    },

    _setText : function(text) {
        ///<summary>Private method setText - Sets the textbox, prefix var to text</summary>
        ///<param name="text">text string</param>
        this.get_element().value = text;
        this._currentPrefix = text;
    },

    _setReturnLocationCode : function(locationCode) {
        ///<summary>Private method setReturnLocationCode</summary>
        ///<param name="locationCode">location code string</param>
        ///<remarks>Sets the value attribute of element identified by the _returnLocationCodeId</remarks>
        if (this._returnLocationCodeId) {
            var returnLocationCodeElement = $get(this._returnLocationCodeId);
            if (returnLocationCodeElement) {
                returnLocationCodeElement.value = locationCode;
            }
        }
    },

    _getReturnLocationCode : function() {
        ///<summary>Private method getReturnLocationCode</summary>
        ///<remarks>gets the value attribute of element identified by the _returnLocationCodeId</remarks>
        if (this._returnLocationCodeId) {
            var returnLocationCodeElement = $get(this._returnLocationCodeId);
            if (returnLocationCodeElement) {
                return returnLocationCodeElement.value;
            }
        }
        return null;
    },
    
    _update : function(returnCompletion, searchText, showAll) {
        ///<summary>Private method update</summary>
        ///<param name="returnCompletion">result object from web service call</param>
        ///<param name="prefixText">text use type for search</param>
        ///<param name="showAll">bool for showing all return locations</param>
        ///<remarks>this method determines what message to display and calls to build the list
        ///     if list length > 0.  hides or shows return container based results

        // reset the message and list elements
        this._reset();
        this._listElement.style.height = "auto";
        this._listElement.style.overflowX = "hidden";
        this._listElement.style.overflowY = "hidden";
        // safari and opera do not recognize overflowX and overflowY
        if (Sys.Browser.agent == Sys.Browser.Safari || Sys.Browser.agent == Sys.Browser.Opera) {
            this._listElement.style.overflow = "hidden";
        }

        if (returnCompletion) {
            this._returnLocations = returnCompletion;
            this._showAllLocations = showAll;
            
            if (returnCompletion.Count == 0) {
                //show no locations message
                this._buildMessage(104);
                this._show(true, false);
            }
            else if (returnCompletion.Count > this._itemPredictiveThreshold) {
                if ((searchText.length >= this._minimumPrefixLength) || showAll) {
                    if (returnCompletion.Locations.length > 0) {
                        //show many locations message and process list
                        this._buildMessage(101);
                        this._createListItems(returnCompletion.Locations);
                        if (returnCompletion.Locations.length > this._itemPredictiveThreshold) {
                            this._listElement.style.height = "225px";
                            //this._listElement.style.overflow = "auto";
                            this._listElement.style.overflowY = "scroll";
                            // safari and opera do not recognize overflowX and overflowY
                            if (Sys.Browser.agent == Sys.Browser.Safari || Sys.Browser.agent == Sys.Browser.Opera) {
                                this._listElement.style.overflow = "auto";
                            }
                        }
                        this._show(true, true);
                    }
                    else {
                        //show no match message
                        this._buildMessage(103);
                        this._createMoreLinkItem();
                        this._show(true, true);
                    }
                }
                else {
                    this._hide();
                }
                //turn on timer
                this._timerOn();
            }                    
            else if (returnCompletion.Count <= this._itemPredictiveThreshold) {
                //show few locations message and process list
                this._buildMessage(102);
                this._createListItems(returnCompletion.Locations);
                this._show(true, true);
            }
        }
        else {
            this._hide();
        }
    },
    
    _updateUsingFirst : function(returnCompletion, searchText) {
        ///<summary>Private method _updateFirst</summary>
        ///<param name="returnCompletion">result object from web service call</param>
        ///<param name="searchText">text for search</param>
        ///<remarks>this method determines what message to display and calls to build the list
        ///     if list length > 0.  select the first location in the list</remarks>
        this._reset();

        if (returnCompletion) {
            this._returnLocations = returnCompletion;
            if (returnCompletion.Count == 0) {
                //show no locations message
                this._buildMessage(104);
                this._show(true, false);
            }
            else if (searchText.length >= this._minimumPrefixLength) {
                if (returnCompletion.Locations.length > 0) {
                    this._locationSelected(returnCompletion.Locations[0]);
                }
                else {
                    //show no match message
                    this._buildMessage(103);
                    this._createMoreLinkItem();
                    this._show(true, true);
                }
            }
            else {
                this._hide();
            }
        }
        else {
            this._hide();
        }
    },
    
    _createListItems : function(completionItems) {
        ///<summary>Private method createListItems</summary>
        ///<param name="completionItems">list of locations</param>
        ///<remarks>this method builds _listElement element with the results list.  each result
        ///     item generates a div element with the location data.  the first element in the 
        ///     item div element is a hidden input field with the serialized location data.  this is
        ///     used later upon user selection.  The location image is formatted with the sitecore
        ///     image library.</remarks>
        this._selectIndex = -1;
        if (completionItems && completionItems.length) {
            var targetElement = this.get_element();
            for (var i = 0; i < completionItems.length; i++){
                var itemElement = document.createElement('div');
                if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
                    itemElement.className = this._ieItemCssName;
                }
                else {
                    itemElement.className = this._itemCssName;
                }
                itemElement.id = targetElement.id + this._itemIdentifier + completionItems[i].Code;

                // store data in hidden field
                var dataElement = document.createElement("input");
                dataElement.setAttribute("type", "hidden");
                dataElement.setAttribute("value", Sys.Serialization.JavaScriptSerializer.serialize(completionItems[i]));
                itemElement.appendChild(dataElement);
                
                if (completionItems[i].IsAirportLocation) {
                    var locationImage = document.createElement("img");
                    //format image path for sitecore using path :: ~/media/Images/Reservation/airport%20gif.ashx
                    //format image path for sitecore using guid :: ~/media/F139684850BC4A79BC24095339A42C26.ashx
                    //  had to remove curly brackets ({}) and dashes (-) from GUID in sitecore
                    //using guid method....deemed faster
                    locationImage.setAttribute("src", ("/Images/Dollar/Images/Common/F139684850BC4A79BC24095339A42C26.gif"));  
                    itemElement.appendChild(locationImage);
                }
                itemElement.appendChild(this._buildListName(completionItems[i].Name, completionItems[i].Code, completionItems[i].MatchByText, completionItems[i].MatchCode, completionItems[i].IsAirportLocation));
                
                this._listElement.appendChild(itemElement);
            }
        }    
    },
    
    _getListItemElement : function(element) {
        ///<summary>Private method getListItemElement</summary>
        ///<param name="element">Associated child DOM element to start search</param>
        ///<remarks>search for element with id having itemIdentifer as part of its string id
        /// this would be the list element in the location _listElement list</remarks>
        if (element) {
            var targetElement = this.get_element();
            if (element.id.indexOf(this._itemIdentifier) >= 0) {
                return element;
            }
            else {
                //search parents for item element...stop at list control.
                var locationSearchListId = targetElement.id + this._listElementIdSuffix;
                while ((element.id != locationSearchListId) && (element.tagName != 'BODY')) {
                    element = element.parentNode;
                    if (element.id.indexOf(this._itemIdentifier) >= 0) {
                        return element;
                    }
                }
            }
        }
        return null;
    },   
    
    _listLocationSelected : function(itemElement) {
        ///<summary>Private method locationSelected</summary>
        ///<param name="itemElement">List element div element selected</param>
        ///<remarks>this method processes the selection of an item from the location
        ///  list.  the hidden location data is retrieved and values set</remarks>
        var inputElements = itemElement.getElementsByTagName("input");
        if (inputElements && inputElements.length) {
            //serialized data in first child element
            var dataElement = inputElements[0];
            var location = Sys.Serialization.JavaScriptSerializer.deserialize(dataElement.getAttribute("value"))
            if (location) {
                this._locationSelected(location);
            }
        }
    },

    _locationSelected : function(location) {
        ///<summary>Private method locationSelected</summary>
        ///<param name="location">location object selected</param>
        ///<remarks>this method processes the selection of a location</remarks>
        if (location) {
            this._timerOff();
            this._hide();
            this._setText(location.Name);
            this._setReturnLocationCode(location.Code);
        }
    },

    _createMoreLinkItem : function() {
        ///<summary>Private method createMoreLinkItem</summary>
        ///<remarks>this method builds a link message in the _listElement element.</remarks>
         var targetElement = this.get_element();

        var itemElement = document.createElement('div');
        if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
            itemElement.className = this._ieItemCssName;
        }
        else {
            itemElement.className = this._itemCssName;
        }
        itemElement.id = targetElement.id + this._itemIdentifier + this._itemLinkIdentifier;

        var messageElement = document.createElement("div");
        messageElement.className = this._descriptionCssName;
        messageElement.appendChild(document.createTextNode(this._noMatchLinkText));

        itemElement.appendChild(messageElement);
        this._listElement.appendChild(itemElement);
    },
    
    _buildMessage : function(messageId) {
        ///<summary>Private method _buildMessage</summary>
        ///<remarks>this method builds the message element.  note that the messages 
        /// received from code-behind from sitecore include render html tags.</remarks>
        var msgElement = document.createElement("div");
        msgElement.className = this._messageCssName;
        msgElement.innerHTML = this._getMessage(messageId);
        this._messageElement.appendChild(msgElement);
    },
    
    _getMessage : function(messageId) {
        ///<summary>Private method _getMessage</summary>
        ///<param name="messageId">id of the message text</param>
        ///<remarks>get the message string based on the message id param</remarks>
        var message = null;
        switch (messageId) { 
            case 101: //Many locations
                message = this._manyLocationsText;                 
                break;
            case 102: //Few locations
                message = this._fewLocationsText;                 
                break;
            case 103: //No Matches
                message = this._noMatchText;                 
                break;
            case 104: //No locations
                message = this._noLocationFoundText;                 
                break;
            case 105: //No Pickup Location
                message = this._noPickupLocationText;                 
                break;
            default:
                message = null;                 
                break;
        }
        return message;
    },
    
    _setHtmlMessageContent : function(msgText) {
        ///<summary>Private method _setHtmlMessageContent</summary>
        ///<param name="msgText">message string to display in message element</param>
        ///<remarks>set the inner html of the message element with the msg text.  text should be html formatted</remarks>
        if (this._messageElement) {
            var msgDescriptionElement = document.createElement("div");
            msgDescriptionElement.className = this._messageCssName;
            msgDescriptionElement.innerHTML = msgText;
            this._messageElement.appendChild(msgDescriptionElement);     
        }
    },    
    
    _setWebServiceError : function() {
        ///<summary>Private method _setWebServiceError</summary>
        ///<remarks>set the message container to display web service error message</remarks>
        this._reset();
        this._setHtmlMessageContent(this._webServiceErrorText);
        this._hideList();
        this._show(true, false);
    },    
    
    _buildListName : function(locationName, locationCode, matchByText, matchCode, isAirport) {
        ///<summary>Private method buildListName</summary>
        ///<param name="locationName">name of location from retrieved list</param>
        ///<param name="locationCode">code of location from retrieved list</param>
        ///<param name="matchByText">text match</param>
        ///<param name="matchCode">field code for match</param>
        ///<param name="isAirport">true if airport location, else false</param>
        ///<returns>DOM element of type div</returns>
        ///<remarks>this method builds the div tags for the location name</remarks>
        var locationDescriptionElement = document.createElement("div");
        locationDescriptionElement.className = this._descriptionCssName;
        
        var name = locationName;
        switch (matchCode) {
            case "City":
                name = matchByText + " - " + name;
                break;
            default:
                break;
        }
        locationDescriptionElement.appendChild(document.createTextNode(this._buildName(name, locationCode, isAirport)));

        return locationDescriptionElement;
    },

    _buildName : function(name, locationCode, isAirport) {
        ///<summary>Private method buildName</summary>
        ///<param name="name">name of location,metro,state,country from retrieved list</param>
        ///<param name="locationCode">code of location from retrieved list</param>
        ///<param name="isAirport">true if airport location, else false</param>
        ///<returns>string of name</returns>
        var listItemName = name;
        
        if (isAirport) {
            listItemName = String.format("{0} ({1})", name, locationCode);
        }
        else {
            listItemName = name;            
        }
        return listItemName;
    },



    _highlightItem : function(item) {
        ///<summary>Private method highlightItem</summary>
        ///<param name="item">DOM element to highlight</param>
        ///<remarks>this method highlights the item if highlightBgColor exists.</remarks>
        if (this._highlightItemBgColor) {
            var children = this._listElement.childNodes;
            
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                if (child != item){
                    child.style.backgroundColor = this._defaultItemBgColor;
                }
            }
            item.style.backgroundColor = this._highlightItemBgColor;
        }
    },
    
    _getPickupLocationCode : function() {
        ///<summary>Private method getPickupLocationCode</summary>
        ///<remarks>get the element value from pickup location element id</remarks>    
        if (this._pickupLocationCodeId) {
            var pickupLocationCodeElement = $get(this._pickupLocationCodeId);
            if (pickupLocationCodeElement) {
                return pickupLocationCodeElement.value;
            }
        }    
        return null;
    },
    
    _callReturnLocationsWebService : function(pickupLocation, searchText, getAll) {
        ///<summary>Private method _callReturnLocationsWebService</summary>
        ///<param name="pickupLocation">pickup location code</param>
        ///<param name="searchText">search text string</param>
        ///<param name="getAll">get all return locations bool</param>
        ///<remarks>this method calls the web service CustomerWebService to get return locations.  if 
        /// get all locations and all locations have already been downloaded, use the currently downloaded
        /// list of all return locations and call the update method, else call proxy for CustomerWebService.asmx
        /// GetReturnLocations(parm1, parm2, parm3, complete handler, error handler, user context);</remarks>
        if (getAll && this._allRtnLocations) {
            this._update(this._allRtnLocations, searchText, getAll);
        }
        else {
            DTG.Dollar.Web.Consumer.Services.CustomerWebService.GetReturnLocations(
                pickupLocation,
                searchText,
                getAll, 
                this._filterB2B,
                this._onLocationReturnComplete, 
                this._onMethodError, 
                [this, pickupLocation, searchText, getAll]);
        }
    },
    
    _getItemIndex : function(itemElementId) {
        ///<summary>Private method _getItemIndex</summary>
        ///<param name="itemElementId">id of dom element to get index in list element list</param>
        ///<remarks>this method determines the index of the itemElement param in the list item element list</remarks>
        var index = -1;
        var children = this._listElement.childNodes;
        for (var i = 0; i < children.length; i++) {
            if (children[i].id == itemElementId){
                index = i;
                break;
            }
        }
        return index;
    },
    
    _scrollItemInToView : function(item, alignToTop) {
        if (item) {
            if ((this._listElement.style.overflow == "auto") || (this._listElement.style.overflow == "scroll")) {
                //make sure the item is in view
                item.scrollIntoView(alignToTop);
            }
        }
    },
    
    _processSelectionByText : function() {
        ///<summary>Private method _processSelectionByText</summary>
        ///<remarks>this method processes the text enter in target element
        ///     and calls web service for list of return locations</remarks>
        var searchText = this._getText();
        if (searchText.length >= this._minimumPrefixLength) {
            this._currentPrefix = searchText;
            if (this._showAllLocations && this._allRtnLocations) {
                this._updateUsingFirst(this._allRtnLocations, searchText);
            }
            else {
                // proxy for CustomerWebService.asmx GetReturnLocations(parm1, parm2, parm3, complete handler, error handler, user context);</remarks>
                DTG.Dollar.Web.Consumer.Services.CustomerWebService.GetReturnLocations(
                    this._pickupLocationCode,
                    searchText,
                    this._showAllLocations, 
                    this._filterB2B,
                    this._onProcessSelectionByText, 
                    this._onMethodError, 
                    [this, this._pickupLocationCode, searchText, this._showAllLocations]);
            }
        }
    },
    
    _isListNode : function(element) {
        ///<summary>Private method _isListNode</summary>
        ///<param name="element">Associated child DOM element to start search</param>
        ///<remarks>search for element with id equal to list element.  this determines if
        /// the element parm is a child of the list element</remarks>
        if (element) {
            if (element.id == this._listElement.id) {
                return true;
            }
            else {
                //search parents for list element...stop at list control.
                while ((element.id != this._listElement.id) && (element.tagName != 'BODY')) {
                    element = element.parentNode;
                    if (element.id == this._listElement.id) {
                        return true;
                    }
                }
            }
        }
        return false;
    },   
        
    preLoad : function(pkupLocationCode) {
        ///<summary>Public method preLoad</summary>
        ///<remarks>this method calls the web services to prime the return locations on the server.</remarks>
        if (pkupLocationCode && pkupLocationCode.length > 0) {
            //check that pickup location code is not a metro area, state, or country
            if ((pkupLocationCode.startsWith(this._countryName)) || (pkupLocationCode.startsWith(this._stateName)) ||
                (pkupLocationCode.startsWith(this._metroName))) {
                //no action....not a valid location...no return locations to preload
            }
            else {        
                // proxy for CustomerWebService.asmx PrimeReturnLocations(parm1, complete handler, error handler, user context);</remarks>
                DTG.Dollar.Web.Consumer.Services.CustomerWebService.PrimeReturnLocations(
                    pkupLocationCode,
                    null, 
                    this._filterB2B,
                    this._onMethodError, 
                    [this, pkupLocationCode]);
            }
        }
    },
    
    clear : function() {
        ///<summary>Public method clear</summary>
        ///<remarks>this method clears the target element value and the return location hidden value.</remarks>
        this._setReturnLocationCode("");
        this._setText("");
    },
   
    //*****************************************************************
    // Event handler methods
    //
    _onTimerTick : function(sender, eventArgs) {
        ///<summary>Private event method _onTimerTick</summary>
        ///<param name="sender">sender object</param>
        ///<param name="eventArgs">event arguments</param>
        ///<remarks>event handler for timer tick</remarks>    
        var text = this._getText();
        if (text.length > 0) {
            if (this._lastPickupLocationCode != this._pickupLocationCode) {
                this._timerOff();     //turn off timer
                //first time call for this pickup location...get initial return locations
                this._setReturnLocationCode("");
                this._lastPickupLocationCode = this._pickupLocationCode;
                this._currentPrefix = text;
                //first time call for this pickup location
                if (text.length < this._minimumPrefixLength) {
                    this._callReturnLocationsWebService(this._pickupLocationCode, "", false);
                }
                else {
                    this._callReturnLocationsWebService(this._pickupLocationCode, text, false);
                }
            }
            else if (this._currentPrefix != text) {
                this._setReturnLocationCode("");
                this._currentPrefix = text;
                if (text.length >= this._minimumPrefixLength) {
                    this._timerOff();     //turn off timer
                    this._callReturnLocationsWebService(this._pickupLocationCode, text, false);
                }
                else {
                    this._update(null, "", false);
                    this._setReturnLocationCode("");
                }
            }
        }
        else {
            this._update(null, "", false);
            this._setReturnLocationCode("");
        }
    },    
    
    _onLocationReturnComplete : function(result, userContext, methodName) {
        ///<summary>Private event method onLocationReturnComplete</summary>
        ///<param name="result">Result of web service call</param>
        ///<param name="userContext">value passed to web service call for user context</param>
        ///<param name="methodName">name of method that called the web service</param>
        ///<remarks>this is the completed method call from web service call.  it processes
        ///  the results of the Location Return Search web service call.  if show all
        ///  is true, save the results to the all return locations variable</remarks>
        var acBehavior = userContext[0];
        //var pickupLocationCode = userContext[1];
        var searchText = userContext[2];
        var showAll = userContext[3];

        if (showAll) {
            acBehavior._allRtnLocations = result;
        }
        acBehavior._update(result, searchText, showAll);
    },

    _onProcessSelectionByText : function(result, userContext, methodName) {
        ///<summary>Private event method _onProcessSelectionByText</summary>
        ///<param name="result">Result of web service call</param>
        ///<param name="userContext">value passed to web service call for user context</param>
        ///<param name="methodName">name of method that called the web service</param>
        ///<remarks>this is the completed method call from a web service call.  it processes
        ///  the results of the selection by text.  if show all is true, save the results
        ///  to the all return locations variable</remarks>
        var acBehavior = userContext[0];
        //var pickupLocationCode = userContext[1];
        var searchText = userContext[2];
        var showAll = userContext[3];
        
        if (showAll) {
            acBehavior._allRtnLocations = result;
        }
        acBehavior._updateUsingFirst(result, searchText);
    },    
    
    _onListMouseDown : function(evnt) {
        ///<summary>Private event method _onListMouseDown</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>processes the item in location list on mouse down event, selecting item</remarks>
        if (evnt) {
            if (evnt.target.id != this._listElement.id) {
                var listItemElement = this._getListItemElement(evnt.target);
                if (listItemElement) {
                    //check if list item is a link item or location item
                    if (listItemElement.id.indexOf(this._itemLinkIdentifier) >= 0) {
                        this._timerOff();     //turn off timer
                        //user selected item link ..... link to show all return locations
                        this._callReturnLocationsWebService(this._pickupLocationCode, "", true);
                    }
                    else {
                        this._listLocationSelected(listItemElement);
                    }
                }
            }
        }
    },
    
    _onListMouseOver : function(evnt) {
        ///<summary>Private event method _onListMouseOver</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>processes the location list on mouse over event, highlighting item</remarks>
        if (evnt) {
            if (evnt.target.id.indexOf(this._itemIdentifier) >= 0) {
                this._highlightItem(evnt.target);
                this._selectIndex = this._getItemIndex(evnt.target.id);  
            }
            else {
                // find list item element thru parent
                var listItemElement =  this._getListItemElement(evnt.target);
                if (listItemElement) {
                    this._highlightItem(listItemElement);
                    this._selectIndex = this._getItemIndex(listItemElement.id);
                }
            }
        }    
    },
    
    _onBodyClick : function(evnt) {
        ///<summary>Private event method _onBodyClick</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>processes the body click event. Handle clicks for the whole window
        ///  If we received a click that we expected, do nothing. If the click was outside our control, 
        ///  hide the popup, check if selection made and text > min...If yes process first in return list</remarks>
        var targetElement = this.get_element();
        if ((evnt.target.id != targetElement.id) && (!this._isListNode(evnt.target))) {
            this._timerOff();
            this._hide();
            var targetValue = this._getText();
            if (targetValue && targetValue.length >= this._minimumPrefixLength) {
                var returnCode = this._getReturnLocationCode();
                if (returnCode != null) {
                    if (returnCode.length == 0) {
                        //no selected value....process selection by text
                        this._processSelectionByText();                
                    }
                }
            }
            else {
                this._setReturnLocationCode("");
            }
            
            // Detach body click event handler
            if (this._bodyClickHandler) {
                $removeHandler(document.body, 'click', this._bodyClickHandler);
                this._bodyClickHandler = null;
            }
        }
    },
    
    _onGotFocus : function(evnt) {
        ///<summary>Private event method _onGotFocus</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>processes the target element (texbox control) focus event.  first checks if
        /// pickup location exists.  if returning to same control again with same pickup location,
        /// redisplay the last result.</remarks>

        // return location has focus....set click handler
        if (!this._bodyClickHandler) {
            this._bodyClickHandler = Function.createDelegate(this, this._onBodyClick);
            $addHandler(document.body, 'click', this._bodyClickHandler);
        }
        //Compare pickup location code....determine display path
        this._pickupLocationCode = this._getPickupLocationCode();
        if (this._pickupLocationCode && (this._pickupLocationCode.length > 0)) {
            //check that pickup location code is not a metro area, state, or country
            if ((this._pickupLocationCode.startsWith(this._countryName)) || (this._pickupLocationCode.startsWith(this._stateName)) ||
                (this._pickupLocationCode.startsWith(this._metroName))) {
                //show msg 104....not a valid location...no return locations display
                this._reset();
                this._setReturnLocationCode("");
                this._setText("");
                this._buildMessage(104);
                this._show(true, false);
            }
            else {
                if (this._lastPickupLocationCode == this._pickupLocationCode) {
                    var searchText = this._getText();
                    if (searchText == this._currentPrefix) {
                        this._update(this._returnLocations, searchText, this._showAllLocations);
                    }
                }
                else {
                    //new pickup location...clear all return locations variable
                    this._allRtnLocations = null;
                    this._setText("");
                    this._setReturnLocationCode("");
                }
                this._timerOn();
            }
        }
        else {
            this._lastPickupLocationCode = "";
            this._reset();
            this._setReturnLocationCode("");
            this._setText("");
            this._buildMessage(105);
            this._show(true, false);
        }
    },
    
    _onLostFocus : function(evnt) {
        ///<summary>Private event method _onLostFocus</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>processes the target element (texbox control) blur (lost focus) event.</remarks>
        this._timerOff();
        //var targetElement = this.get_element();
        var targetValue = this._getText();
        if (targetValue && targetValue.length > 0) {
        }
        else {
            this._setReturnLocationCode("");
        }
    },
    
    _onMethodError : function(error, userContext, methodName) {
        ///<summary>Private event method _onMethodError</summary>
        ///<param name="error">error object from web service call</param>
        ///<param name="userContext">value passed to web service call for user context</param>
        ///<param name="methodName">name of method that called the web service</param>
        ///<remarks>this method is called when an error has been received from the 
        /// web service call</remarks>
        Sys.Debug.trace(error.get_message());
        var acBehavior = userContext[0];
        acBehavior._setWebServiceError();
    },
    
    _onKeyPress : function(evnt) {
        ///<summary>Private event method _onKeyPress - called on key press of target handler</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>event method used to capture enter key for all browsers</remarks>
        if (evnt) {
            var key = evnt.keyCode ? evnt.keyCode : evnt.rawEvent.keyCode;
            if (key === Sys.UI.Key.enter) {         //13
                if (this._selectIndex != -1) {
                    var selectedItem = this._listElement.childNodes[this._selectIndex];
                    if (selectedItem) {
                        if (selectedItem.id.indexOf(this._itemLinkIdentifier) >= 0) {
                            //this._timerOff();     //turn off timer
                            //user selected item link ..... link to show all return locations
                            this._callReturnLocationsWebService(this._pickupLocationCode, "", true);
                        }
                        else {
                            this._listLocationSelected(this._listElement.childNodes[this._selectIndex]);
                        }
                    }
                    evnt.stopPropagation();     
                    evnt.preventDefault();
                }
                else {
                    //this._timerOff();
                    evnt.stopPropagation();     
                    evnt.preventDefault();
                    this._processSelectionByText();                
                }                    
            }
        }
    },
    
    _onKeyDown : function(evnt) {
        ///<summary>Private event method _onKeyDown - called on key down of target handler</summary>
        ///<param name="evnt">event object</param>
        ///<remarks>event method used to capture enter key for all browsers.  note that Safari does not
        /// respond to the enter key(13) in this keydown event.  check keypress event.</remarks> 
        if (evnt) {
            var key = evnt.keyCode ? evnt.keyCode : evnt.rawEvent.keyCode;
            if (key === Sys.UI.Key.esc) {        //27
                this._hide();
                evnt.preventDefault();
            }
            else if (key === Sys.UI.Key.up) {   //38
                if (this._selectIndex > 0) {
                    this._selectIndex--;
                    this._highlightItem(this._listElement.childNodes[this._selectIndex]);
                    this._scrollItemInToView(this._listElement.childNodes[this._selectIndex], false);
                    evnt.stopPropagation();     
                    evnt.preventDefault();
                }
            }
            else if (key === Sys.UI.Key.down) { //40
                if (this._selectIndex < (this._listElement.childNodes.length - 1)) {
                    this._selectIndex++;
                    this._highlightItem(this._listElement.childNodes[this._selectIndex]);
                    this._scrollItemInToView(this._listElement.childNodes[this._selectIndex], false);
                    evnt.stopPropagation();     
                    evnt.preventDefault();
                }
            }
            else if (key === Sys.UI.Key.tab) {  //9
                this._timerOff();
                this._hide();
                if (this._selectIndex == -1) {
                    this._processSelectionByText();                
                }                                        
                else {                
                    this._listLocationSelected(this._listElement.childNodes[this._selectIndex]);
                    this._hideList();
                }
            }
            else {
                this._timerOn();
            }
        }
    }
}

DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior.registerClass('DTG.Dollar.Web.Consumer.Common.Ajax.LocationReturnSearch.LocationReturnSearchBehavior', AjaxControlToolkit.BehaviorBase);

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();