﻿var CascadingDropDown = Class.create();

CascadingDropDown.DefaultPromptText = "          ";
CascadingDropDown.DefaultLoadingText = "Loading...";
CascadingDropDown.DefaultServiceUrl = "CascadingDropDownHandler.ashx";
CascadingDropDown.CompanyId = 1;
CascadingDropDown.UserContextId = 1;
CascadingDropDown.UserId = 1;
CascadingDropDown.ProductId = 1;
CascadingDropDown.TemplateId = 1;
CascadingDropDown.RevisionType = 2;
CascadingDropDown.ClientCache = new Array();
CascadingDropDown.CustomEventsEnabled = false;
CascadingDropDown.CustomEventsNamespace = "CascadingDropDown:";

CascadingDropDown.prototype = {
    initialize: function (controlId, parentControlId, variableName, parentVariableName, promptTextEnabled, rememberLastSelected, uiRulesEnabled, populateItems) {
        /// <summary>
        /// Initialize the drop-down behavior
        /// </summary>
        /// <returns />

        // Properties
        this._isInitialized = false;
        this._controlId = controlId;
        this._parentControlId = parentControlId;
        this._variableName = variableName;
        this._parentVariableName = parentVariableName;
        this._promptTextEnabled = true;
        this._promptText = CascadingDropDown.DefaultPromptText;
        this._loadingText = CascadingDropDown.DefaultLoadingText;
        this._serviceUrl = CascadingDropDown.DefaultServiceUrl;
        this._selectedValue = "";
        this._rememberLastSelected = true;
        this._uiRulesEnabled = false;
        this._recalcPricingGrid = false;

        if (typeof (promptTextEnabled) !== 'undefined')
            this._promptTextEnabled = promptTextEnabled;
        if (typeof (rememberLastSelected) !== 'undefined')
            this._rememberLastSelected = rememberLastSelected;
        if (typeof (uiRulesEnabled) !== 'undefined')
            this._uiRulesEnabled = uiRulesEnabled;

        var el = this.get_element();

        // attach additional properties on element so that children controls (if any) can have easy access
        el.CascadingDropDownCategory = variableName;
        el.CascadingDropDownParentControlId = this._parentControlId;

        // clear any items in the drop down (if specified; default is no)
        if (populateItems && true == populateItems)
            this._clearItems();

        // Attach change handler to self
        Event.observe(el, 'change', this._onChange.bindAsEventListener(this), false);

        var parentSelectedValue = '';
        if (this._parentControlId.length > 0) {
            var parentEl = this.get_ParentElement();
            if (parentEl && parentEl != null) {
                if (!parentEl.childDropDowns) {
                    parentEl.childDropDowns = new Array();
                    parentEl.childDropDownsCaseInsensitive = new Array();
                }
                parentEl.childDropDowns[this._controlId] = this;
                parentEl.childDropDownsCaseInsensitive[this._controlId.toLowerCase()] = this;

                // try and grab our parent's selected value
                if (-1 != parentEl.selectedIndex) {
                    parentSelectedValue = parentEl.options[parentEl.selectedIndex].value;
                    // if we don't have a selected value and our parent is selected then try and populate ourself
                    if (-1 == el.selectedIndex && parentSelectedValue != '' && parentSelectedValue != ' ')
                        populateItems = true;
                }
            }
        }

        this._isInitialized = true;
    },

    get_element: function () {
        if (this._controlId.length < 1)
            return null;
        else
            return $(this._controlId);
    },

    get_ParentElement: function () {
        if (this._parentControlId.length < 1)
            return null;
        else
            return $(this._parentControlId);
    },

    get_ChildDropDown: function (childControlId) {
        var el = this.get_element();

        if (el.childDropDowns) {
            // try and return the child dropdown by the given name (case sensitive)
            if (typeof (el.childDropDowns[childControlId]) !== 'undefined')
                return el.childDropDowns[childControlId];

            // fallback to a normalized case insensitive lookup in case the spreadsheet upload isn't using proper casing
            return el.childDropDownsCaseInsensitive[childControlId.toLowerCase()];
        }
        return null;
    },

    get_isInitialized: function () {
        return this._isInitialized;
    },

    get_PromptText: function () {
        return this._promptText;
    },
    set_PromptText: function (promptText) {
        this._promptText = promptText;
    },

    get_PromptTextEnabled: function () {
        return this._promptTextEnabled;
    },
    set_PromptTextEnabled: function (promptTextEnabled) {
        this._promptTextEnabled = promptTextEnabled;
    },

    get_LoadingText: function () {
        return this._loadingText;
    },
    set_LoadingText: function (loadingText) {
        this._loadingText = loadingText;
    },

    get_RememberLastSelected: function () {
        return this._rememberLastSelected;
    },
    set_RememberLastSelected: function (rememberLastSelected) {
        this._rememberLastSelected = rememberLastSelected;
    },

    get_UIRulesEnabled: function () {
        return this._uiRulesEnabled;
    },
    set_UIRulesEnabled: function (uiRulesEnabled) {
        this._uiRulesEnabled = uiRulesEnabled;
    },

    get_SelectedValue: function () {
        return this._selectedValue;
    },
    set_SelectedValue: function (value, text) {
        if (this._selectedValue != value) {
            if (!text) {
                // Initial population by server; look for "value:::text" pair
                var i = value.indexOf(':::');
                if (-1 != i) {
                    text = value.slice(i + 3);
                    value = value.slice(0, i);
                }
            }
            var oldValue = this._selectedValue;
            this._selectedValue = value;
        }
    },

    _onChange: function (evt) {
        /// <summary>
        /// Handler for the drop down's change event
        /// </summary>
        /// <returns />

        var el = this.get_element();
        // Record the selected value in the client state
        if ((-1 != el.selectedIndex) && !(true == this._promptTextEnabled && (0 == el.selectedIndex))) {
            this.set_SelectedValue(el.options[el.selectedIndex].value, el.options[el.selectedIndex].text);
        } else {
            this.set_SelectedValue('', '');
        }

        if (this._serviceUrl.length > 0 && el.childDropDowns) {
			var startedEventId = "CascadingDropDown:RemoteContentCallStarted";
            $(document).fire(startedEventId);
            
            if (typeof (RemoteContentCallStarted) == "function")
                RemoteContentCallStarted();

            // first check cache to see if entry exists
            var cacheKey = "Element=" + this._controlId + "|Value=" + this.get_SelectedValue();
            if (typeof (CascadingDropDown.ClientCache[cacheKey]) !== 'undefined') {
                // we've already made at least one async call for these options, 
                //   so let's just use the last result
                setChildDropDowns(this, CascadingDropDown.ClientCache[cacheKey]);
                return;
            }

            // if the dropdown has not been populated or nothing is selected, 
            //      don't make an async call - just clear out the children
            if (!this._isPopulated() || this.get_SelectedValue() == '') {
                setChildDropDowns(this, null);
                return;
            }
            else {
                // gettingList == true shows the loading text (if any)
                setChildDropDowns(this, null, true);
            }

            // Create the service parameters and optionally add the context parameter
            var paramDictionary = {
                companyId: CascadingDropDown.CompanyId,
                userContextId: CascadingDropDown.UserContextId,
                userId: CascadingDropDown.UserId,
                productId: CascadingDropDown.ProductId,
                templateId: CascadingDropDown.TemplateId,
                revisionType: CascadingDropDown.RevisionType,
                uiRulesEnabled: this._uiRulesEnabled,
                parentVariableName: this._variableName,
                parentListSelectedValue: this.get_SelectedValue()
            };

            //var dictStr = '';
            //for (var dictKey in paramDictionary)
            //    dictStr += '\t'+dictKey+'='+paramDictionary[dictKey]+'\r\n';

            var errorMsg = "There was a Form Preview error re-populating Cascading Drop-Downs.";
            // Call the ASHX request handler
            var ddObj = this;
            var asyncRequest = new Ajax.Request(this._serviceUrl,
            {
                parameters: paramDictionary,
                onSuccess: function (transport) {
                    // Success, update the DropDownList                    
                    try {
                        var jsonResult = transport.responseText.evalJSON();
                        setChildDropDowns(ddObj, jsonResult);
                        CascadingDropDown.ClientCache[cacheKey] = jsonResult; // add to cache for faster retrieval next time through                        
                    } catch (e) {
                        alert(errorMsg);
                        if (typeof (RemoteContentCallCompleted) == "function")
                            RemoteContentCallCompleted(e);
                    }
                },
                onException: function (req, exception) {                    
                    alert(errorMsg);
                    if (typeof (RemoteContentCallCompleted) == "function")
                        RemoteContentCallCompleted(exception);

                    return true;
                },
                onFailure: function (transport) {
                    alert(errorMsg);
                    if (typeof (RemoteContentCallCompleted) == "function")
                        RemoteContentCallCompleted(new Error(errorMsg));
                }
            });
        }
    },

    _clearItems: function () {
        var e = this.get_element();
        while (0 < e.options.length) {
            e.remove(0);
        }
    },

    _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 (true == this._promptTextEnabled) {
            return items > 1;
        } else {
            return items > 0;
        }
    }
};
//);

function setChildDropDowns(parentDDObj, cascadingChildLists, gettingList) {    
    var listLength = 0;
    
    if (cascadingChildLists)
        listLength = cascadingChildLists.size ? cascadingChildLists.size() : cascadingChildLists.length;

    // load any cascade children    
    var childrenLoaded;
    var doRecalcPricing = parentDDObj._recalcPricingGrid;    
    if (listLength > 0) {
        childrenLoaded = new Array();
        for (var i = 0 ; i < listLength ; i++) {
            var childList = cascadingChildLists[i];
            var childDDObj = parentDDObj.get_ChildDropDown(childList.HtmlId);
            
            // the child dropdown may not exist;
            // we actually don't want to error in this case
            // since they may be re-using lists across templates
            // and some templates will not have all the fields (just a subset)
            // so we'll just update ones that actually exist and skip everything else
            if (childDDObj && childDDObj._controlId) {
                setOptions(childDDObj, childList, gettingList);

                if (childDDObj._recalcPricingGrid)
                    doRecalcPricing = true;

                childrenLoaded[childDDObj._controlId] = true;
            }
            else 
            {
                childrenLoaded[childList.HtmlId] = true;
                childrenLoaded[childList.HtmlId.toLowerCase()] = true;
            }
        }
    }
        
    var el = parentDDObj.get_element();
    if (el && el.childDropDowns) {
        // clear out any children that weren't returned from the async call but are in a loading state
        for(var childDDKey in el.childDropDowns) {
            var childDDObj = el.childDropDowns[childDDKey];
            if (childDDObj && childDDObj._controlId) {
                if (listLength == 0 || !childrenLoaded || typeof(childrenLoaded[childDDObj._controlId]) == 'undefined')
                    setOptions(childDDObj, null, gettingList);

                if (childDDObj._recalcPricingGrid)
                    doRecalcPricing = true;
            }
        }

        // some other js client-side objects may want to know when a dropdown is re-populated
        if (!gettingList && listLength > 0 && true == CascadingDropDown.CustomEventsEnabled) {
            for (var childDDKey in el.childDropDowns) {
                var childDDObj = el.childDropDowns[childDDKey];
                if (childDDObj && childDDObj._controlId) {
                    var eventId = CascadingDropDown.CustomEventsNamespace + childDDObj._controlId;
                    $(childDDKey).fire(eventId);                                       
                }
            }
        }
    }

    if (doRecalcPricing && typeof (RecalcPricingGrid) == "function")
        RecalcPricingGrid();

	var completedEventId = CascadingDropDown.CustomEventsNamespace + "RemoteContentCallCompleted";
    $(document).fire(completedEventId);
    
    if ((arguments.length < 3 || !gettingList) && typeof (RemoteContentCallCompleted) == "function")
        RemoteContentCallCompleted();
}

function setOptions(ddObj, ddOptions, gettingList) {
    /// <summary>
    /// Set the contents of the DropDownList to the specified list
    /// </summary>
    /// <param name="ddOptions" mayBeNull="true" elementType="Object">
    /// Array of options (where each option has name and value properties)
    /// </param>
    /// <param name="gettingList" type="Boolean" optional="true">
    /// Whether we are fetching the list of options from the web service
    /// </param>
    /// <returns />

    if (!ddObj.get_isInitialized()) {
        return;
    }

    var e = ddObj.get_element();
    // Remove existing contents
    ddObj._clearItems();

    // some dropdowns want to clear out/disregard their selected state every time they get refreshed
    if (!ddObj.get_RememberLastSelected() ) {
        ddObj.set_SelectedValue('', '');
    }
    
    // Populate prompt text (if available)
    var headerText;
    if (gettingList && ddObj._loadingText) {
        headerText = ddObj._loadingText;
    } else if (true == ddObj._promptTextEnabled) {
        headerText = ddObj._promptText;
    }
    if (headerText) {
        var optionElement = new Option(headerText, "");
        e.options[e.options.length] = optionElement;
    }

    // add each item to the DropDownList, selecting the previously selected item
    var selectedValueOption = null;
    var defaultIndex = -1;
    var listLength = 0;

    if (ddOptions) {        
        listLength = ddOptions.Options.size ? ddOptions.Options.size() : ddOptions.Options.length;
        for (var i = 0 ; i < listLength ; i++) {
            var listItemValue = ddOptions.Options[i].Value;
            var listItemName = ddOptions.Options[i].Text;
            if(typeof(listItemName) == 'undefined' || '' == listItemName || ' ' == listItemName)
                listItemName = listItemValue;         

            var optionElement = new Option(listItemName, listItemValue);            
            if (listItemValue == ddObj.get_SelectedValue())
                selectedValueOption = optionElement;

            e.options[e.options.length] = optionElement;
        }
        if (selectedValueOption)
            selectedValueOption.selected = true;
    }
    
    // if we didn't match the selected value, and we found a default item, select that one.
    if (selectedValueOption) {
        // Call set_SelectedValue to store the text as well
        ddObj.set_SelectedValue(e.options[e.selectedIndex].value, e.options[e.selectedIndex].text);
    } else if (!selectedValueOption && defaultIndex != -1) {
        e.options[defaultIndex].selected = true;
        ddObj.set_SelectedValue(e.options[defaultIndex].value, e.options[defaultIndex].text);
    } else if (!selectedValueOption && !gettingList) {
        ddObj.set_SelectedValue('', '');
    }
    
    // force a change on the dropdown, to trigger its cascading behavior    
    if (!gettingList)
        ddObj._onChange();
}
