MV = MV || {};

MV.EPD = (function() {

    var debug = false;
    var _epd;

    var resizeApplicationSection = function(context) {
        var body = $(window);
        var article = context.find('.content');
        var section = context.find('.section:not(.invisible)');
        var footer = context.find('.footer');

        var footerHeight = footer.height();
        var headerHeight = article.offset().top;
        var articlePadding = article.outerHeight(true) - article.height();

        var fullHeight = body.height();
        var sectionHeight = fullHeight - (footerHeight + headerHeight + articlePadding);

        section.height(sectionHeight);
        _epd.applicationSettings.viewport.sectionHeight = sectionHeight;

        return sectionHeight;
    };

    var resizeApplicationWrapper = function(context) {
        var section = context.find('.section:not(.invisible)');
        var sectionHeader = section.find('.sectionHeader');
        var wrapper = context.find('.wrapper');

        if (sectionHeader && wrapper) {
            var fullHeight = section.height();
            var wrapperHeight = fullHeight;

            wrapperHeight -= sectionHeader.height();

            wrapper.height(wrapperHeight);
            _epd.applicationSettings.viewport.wrapperHeight = wrapperHeight;

            return wrapperHeight;
        }
    };

    var arrayToObjectMerge = function arrayToObjectMerge(array, obj) {
        $.each(array, function() {
            var o = this;
            $.each(o, function(key) {
                obj[key] = o[key];
            });
        });
        return obj;
    };

    var formatWSParameters = function formatWSParameters(paramobj) {
        var returnary = [];
        for (property in paramobj) {
            returnary.push(property);
            returnary.push(paramobj[property]);
        }
        return returnary;
    };

    var resultColumnProfileHash = {
        'column1,column2,column3,column4,column5': 'practitioner',
        'column1,column2,column3': 'nationalFacility',
        'column1,column2,column3,column4': 'other',
        'column1,column2,column3,column4,column6': 'hospital'
    };

    var defaultListeners = (function() {
        var defaultListeners = "onBeforeInit onInit onBeforeProviderTypeShow onProviderTypeShow onProviderTypeHide onProviderTypeValidate onSearchFormValidate onSearchResultsCreate onBeforeSearchResultsCreate onLoadSearchParameters onProviderTypeResetSelection onDetailCreate onDetailShow onDetailPrint onDetailClose onSearchFormUpdate onBeforeSearchFormUpdate onProviderSearchEvent onExternalLinkClicked onHealthwiseLinkClicked";
        defaultListeners += " onInitStart onInitComplete onCreateProviderTypeStart onCreateProviderTypeComplete onResetSearchFormStart onResetSearchFormComplete onShowSearchFormStart onShowSearchFormComplete onRedirectApplicationStart onRedirectApplicationComplete onCreateSearchFormOptionalFieldsStart onCreateSearchFormOptionalFieldsComplete onUpdateSearchFormStart onUpdateSearchFormComplete onCreateDetailPageStart onCreateDetailPageComplete onCreateDetailContentStart onCreateDetailContentComplete onCloseDetailPageStart onCloseDetailPageComplete onWriteDetailContentStart onWriteDetailContentComplete onPrintDetailContentStart onPrintDetailContentComplete onShowDetailPageStart onShowDetailPageComplete onSubmitSearchFormStart onSubmitSearchFormComplete onLoadSearchResultsStart onLoadSearchResultsComplete onLoadSearchParametersStart onLoadSearchParametersComplete onCreateSearchResultsStart onCreateSearchResultsComplete onSortSearchResultsStart onSortSearchResultsComplete onResizeApplicationViewportStart onResizeApplicationViewportComplete onLoadHealthInfoContentStart onLoadHealthInfoContentComplete onHideHealthInfoPageStart onHideHealthInfoPageComplete onResetApplicationStart onResetApplicationComplete onCreateSearchResultsContentStart onCreateSearchResultsContentComplete onCreateHealthInfoPageStart onCreateHealthInfoPageComplete onShowHealthInfoPageStart onShowHealthInfoPageComplete onShowSearchResultsStart onShowSearchResultsComplete onCreateOptionalFieldsComplete";
        var arlisteners = defaultListeners.split(' ');
        var retobj = {};
        for (var i = 0; i < arlisteners.length; i++) {
            retobj[arlisteners[i]] = $.extend(true, {}, MV.Listener);
        };
        return retobj;
    })();

    var defaultSorters = {

        'name': function(p1, p2) {
            if (p1.ProviderName == p2.ProviderName) return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
            else return (p1.ProviderName < p2.ProviderName) ? -1 : 1;
        },
        'distance': function(p1, p2) {
            if (Math.abs(p1.Distance) == Math.abs(p2.Distance)) return (p1.ProviderName < p2.ProviderName) ? -1 : 1;
            else return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
        },
        'specialties': function(p1, p2) {
            var p1SpecialtyArray = [].concat(p1.Specialties || [], p1.BoardCertifiedSpecialties || []);
            var p2SpecialtyArray = [].concat(p2.Specialties || [], p2.BoardCertifiedSpecialties || []);
            var p1Specialty = (p1SpecialtyArray && p1SpecialtyArray.length > 0) ? 1 : -1;
            var p2Specialty = (p2SpecialtyArray && p2SpecialtyArray.length > 0) ? 1 : -1;
            if (p1Specialty == p2Specialty) {
                if ((p1SpecialtyArray[0].Name == p2SpecialtyArray[0].Name) || p1Specialty < 0) {
                    return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
                } else {
                    return (p1SpecialtyArray[0].Name < p2SpecialtyArray[0].Name) ? -1 : 1;
                }
            } else {
                return (p1Specialty < p2Specialty) ? -1 : 1;
            }
        },
        'recognitions': function(p1, p2) {
            if (p1.NumberOfRecognitions == p2.NumberOfRecognitions) {
                return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
            } else {
                return (p1.NumberOfRecognitions > p2.NumberOfRecognitions) ? -1 : 1;
            }
        },
        'office': function(p1, p2) {
            var p1GroupName = (!p1.GroupProviderName) ? -1 : 1;
            var p2GroupName = (!p2.GroupProviderName) ? -1 : 1;
            if (p1GroupName == p2GroupName) {
                if ((p1.GroupProviderName == p2.GroupProviderName) || p1GroupName < 0) return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
                else return (p1.GroupProviderName < p2.GroupProviderName) ? -1 : 1;
            } else {
                return (p1GroupName > p2GroupName) ? -1 : 1;
            }
        },
        'qualityData': function(p1, p2) {
            if (p1.HasQualityData == p2.HasQualityData) {
                return (Math.abs(p1.Distance) < Math.abs(p2.Distance)) ? -1 : 1;
            } else {
                return (p1.HasQualityData > p2.HasQualityData) ? -1 : 1;
            }
        }
    };


    //** RETURN EPD OBJECT **//

    return {

        components: {},
        providerTypes: {},
        activeProviderType: {},
        sorters: $.extend({}, defaultSorters),
        listeners: defaultListeners,
        applicationSettings: {
            wsRenderHash: {},
            clientNavigationHash: {},
            clientDetailAttributesHash: {},
            standardSearchDropdownData: {},
            planId: '',
            planName: '',
            window: { previous: { height: '', width: ''} },
            viewport: {
                sectionHeight: '', wrapperHeight: '',
                healthInfoContentSection: { sectionHeight: '', wrapperHeight: '' },
                directoryListingSection: { sectionHeight: '', wrapperHeight: '' },
                articleDetail: { sectionHeight: '', wrapperHeight: '' }
            }
        },
        searchInfo: {
            initialResultCount: 30,
            fullResultCount: 100,
            displayRowCount: 30,
            returnMaxResults: true,
            returnSortResults: false,
            isNewSearch: true,
            isHealthIssueSearch: true,
            lastSearchParameters: {},
            lastSearchResults: {},
            lastSearchLatLng: {},
            lastSort: "distance",
            defaultSort: "distance",
            isNationalSearch: false
        },

        init: function(opts) {

            if (!$.browser.msie && debug) window.console.profile("Initialize EPD object .init()");

            _epd = this;    // set the closure variable so that we dont have to make all these references to this;

            // load up any listeners passed in as options
            if (opts && opts.onInitStart) { this.addListener('onInitStart', opts.onInitStart); };
            if (opts && opts.onInitComplete) { this.addListener('onInitComplete', opts.onInitComplete); };

            this.listeners.onInitStart.fire();
            this.templates = opts.templates;
            this.applicationSettings.planId = opts.planId;
            this.applicationSettings.planName = opts.planName;
            this.applicationSettings.referrer = opts.referrer;
            this.searchInfo.defaultSort = opts.defaultSort;
            this.searchInfo.lastSort = opts.defaultSort;



            //** LOAD COMPONENTS **//

            this.components.providerType = MV.ProviderType;
            this.components.navigation = MV.ProviderTypeNavigation.init(this.templates.navigation)
            this.components.dataTable = MV.DataTable.init(this.templates.dataTable);
            this.components.searchForm = MV.SearchForm.init(this.templates.searchForm);
            this.components.detail = MV.Detail.init(this.templates.detail);
            this.components.providerTypeValidation = MV.Validation;
            this.components.validation = MV.Validation.init({ template: this.templates.searchForm.validationError, leftOffestReferenceSelector: '.nav_content' });


            //** SET UP PROVIDER TYPES, SORTERS, LISTENERS FROM CONFIG **//
            this.addSorters(defaultSorters);


            //** BIND WINDOW / VIEWPORT EVENTS **//

            $(window).resize(function(e) {
                _epd.resizeApplicationViewport();
            });

            if (!$.browser.msie && debug) window.console.profileEnd();


            //** INITIALIZE FUNCTIONS FOR UI **//

            if (!$.browser.msie && debug) window.console.profile("Initialize EPD Matrix .GetProviderAndNavMatrix()");

            // call init ws to get data onload
            MV.WebServices.EPD.GetProviderAndNavMatrix(this.getPlanId(), '', '', '', function(data) {

                // load the navigation matrix for redirect urls
                var ret = {};
                for (var i = 0; i < data.NavigationMatrix.length; i++) {
                    var d = data.NavigationMatrix[i];
                    var ptcode = d.ClientProviderTypeCode || 'EMPTY';
                    var ncode = d.ClientNetworkId || 'EMPTY';
                    var stcode = d.ClientProviderSubtypeCode || 'EMPTY';

                    if (!ret[ptcode]) { ret[ptcode] = {}; };

                    if (!ret[ptcode][ncode]) { ret[ptcode][ncode] = {}; };

                    if (!ret[ptcode][ncode][stcode]) { ret[ptcode][ncode][stcode] = { redirectUrl: d.NavigationUrl }; };
                }
                _epd.applicationSettings.clientNavigationHash = ret;


                // load the provider types, merge any application-config settings
                for (var i = 0; i < data.ProviderTypes.length; i++) {
                    var d = data.ProviderTypes[i];
                    $.extend(true, d, EPDConfig.providerTypes[d.Code]);
                    _epd.createProviderType(d);
                }

                // filter provider type links based on national alliance flag
                if (_epd.isNationalAlliance()) {
                    for (var i = data.ProviderTypes.length - 1; i >= 0; i--) {
                        if (!data.ProviderTypes[i].National) {
                            data.ProviderTypes.splice(i, 1);
                        }
                    }
                }


                // create the left nav provider type links
                _epd.components.navigation.create(data.ProviderTypes);

                // add the event that triggers the provider type selection
                $('#selectedProviderTypeTitle').bind('click', _epd.resetSearchFormBridge);
                $('#selectedProviderTypeLink').bind('click', _epd.resetSearchFormBridge);

                // add the event that shows the provider type form fields
                $('#localLinks ul, #nationalLinks ul').bind('click', _epd.showSearchFormBridge);

                // app is loaded fire onLoad event
                _epd.listeners.onInitComplete.fire();

                if (!$.browser.msie && debug) window.console.profileEnd();

                // resize the content pane
                _epd.resizeApplicationViewportHelper();
            });

            return this;
        },


        //** PROVIDER TYPE UI SETUP **/

        createProviderType: function(providerTypeConfig) {
            this.listeners.onCreateProviderTypeStart.fire();
            this.providerTypes[providerTypeConfig.id] = $.extend(true, {}, this.components.providerType);
            this.providerTypes[providerTypeConfig.id].load(providerTypeConfig);
            this.listeners.onCreateProviderTypeComplete.fire();
        },

        resetSearchFormBridge: function(e) {
            e.preventDefault();
            _epd.resetSearchForm();
        },

        resetSearchForm: function() {
            this.listeners.onResetSearchFormStart.fire();
            _epd.cancelWSRender('NationalProviderSearch'); // cancel any WS rendering
            _epd.cancelWSRender('ProviderSearch'); // cancel any WS rendering
            _epd.resetApplication();
            this.listeners.onResetSearchFormComplete.fire();
        },

        showSearchFormBridge: function(e) {
            e.preventDefault();
            var typeid = e.target.id;
            _epd.showSearchForm(typeid);
        },

        showSearchForm: function(typeid) {
            if (!$.browser.msie && debug) window.console.profile("Show a provider type .showSearchForm()");

            _epd.cancelWSRender('NationalProviderSearch'); // cancel any WS rendering
            _epd.cancelWSRender('ProviderSearch'); // cancel any WS rendering

            if (!_epd.providerTypes[typeid]) { return false; }

            var currentProviderType = _epd.getProviderTypeById(typeid); //use this variable as a reference
            var currentProviderTypeCode = currentProviderType.providerTypeCode;

            _epd.listeners.onShowSearchFormStart.fire({ planId: _epd.getPlanId(), providerTypeCode: currentProviderTypeCode });

            _epd.setActiveProviderType(currentProviderType);

            // adjust the doctor decoration
            $('#directoryListingSection').find('.decoration').addClass('decorationWithForm');

            // check the navigation matrix to see if we need to do any url redirection based on the provider type 
            // the network and specialty dropdowns should not be checked, pass in EMPTY as a key for specialty
            // and network
            if (_epd.redirectApplication(currentProviderTypeCode, 'EMPTY', 'EMPTY')) {
                return false;
            };

            _epd.components.navigation.selectProviderType(currentProviderType);

            // append the provider type form and set "return" keypress event, submit button event
            $('#providerTypeForm').addClass('invisible')
                .find('div.providerTypeFieldsetContainer').remove().end()
                .append(_epd.components.searchForm.create(currentProviderType.config))
                .unbind('keyup')
                .keyup(function(event) {
                    if (event.keyCode == 13) {
                        _epd.submitSearchForm(event);
                    }
                })
                .end();

            if (!$.browser.msie && debug) window.console.profileEnd();

            MV.WebServices.EPD.GetSearchConfigurationDecoratorWrapper(currentProviderType.config.National, _epd.getPlanId(), currentProviderTypeCode, '', '', function(data) {

                _epd.searchInfo.configurationDecorator = data;

                var listenerBundle = {
                    providerTypeId: typeid,
                    providerTypeCode: currentProviderType.providerTypeCode,
                    searchConfigurationData: data,
                    planId: _epd.getPlanId(),
                    searchFields: (currentProviderType.config.National) ? currentProviderType.config.SearchFields : null
                }
                _epd.setStandardSearchFormDropdownData(listenerBundle.searchConfigurationData);
                _epd.listeners.onShowSearchFormComplete.fire(listenerBundle);
                _epd.providerTypes[typeid].listeners.onShow.fire(listenerBundle);
                _epd.createSearchFormOptionalFields(listenerBundle);
            });

            return false;
        },

        redirectApplication: function(clientProviderTypeCode, clientNetworkId, clientSubtypeId) {
            this.listeners.onRedirectApplicationStart.fire();
            var linkObj = this.getSearchFormRedirectUrl(clientProviderTypeCode, clientNetworkId, clientSubtypeId);
            if (linkObj.url) {
                MV.EPD.openWin({ url: linkObj.url, label: linkObj.label }, 'offsite');
            }

            this.listeners.onRedirectApplicationComplete.fire();
            return linkObj.url;
        },


        //** PROVIDER TYPE SEARCH FORM **/

        createSearchFormOptionalFields: function(params) {
            this.listeners.onCreateSearchFormOptionalFieldsStart.fire();
            if (params.searchFields) {
                _epd.updateSearchForm(params.searchFields);
            } else {
                MV.WebServices.EPD.GetNetworkAndSubtype(params.planId, params.providerTypeCode, $('#specialtyInput').val(), $('#networkInput').val(), function(data) {
                    _epd.updateSearchForm(data.SearchFields);
                });
            }
            this.listeners.onCreateSearchFormOptionalFieldsComplete.fire();
        },

        updateSearchForm: function(formfields, showSubmitButtonOnly) {
            var listenerBundle = {
                searchConfigurationData: _epd.getStandardSearchFormDropdownData()
            };
            _epd.listeners.onUpdateSearchFormStart.fire(listenerBundle);

            this.components.searchForm.createOptionalFields(formfields, showSubmitButtonOnly, function(args) {
                _epd.listeners.onCreateOptionalFieldsComplete.fire(args);
            });
            $('#providerTypeSubmit').bind('click', function(e) {
                _epd.submitSearchForm(e);
            });

            _epd.listeners.onUpdateSearchFormComplete.fire(listenerBundle);
        },

        createSearchFormSubmitButton: function() {
            this.updateSearchForm([], true);
        },


        //** SEARCH FORM VALIDATION **/
        validateElement: function(selector, testType, forceError) {
            return this.components.validation.validateElement(selector, testType, forceError);
        },

        forceValidationError: function(referenceElementSelector, errorMessage) {
            this.components.validation.forceValidationError(referenceElementSelector, errorMessage);
        },

        clearValidationError: function(referenceElementSelector) {
            this.components.validation.clearValidationError(referenceElementSelector);
        },


        //** PROVIDER TYPE DETAIL **/


        createDetailPage: function(params) {
            if (!$.browser.msie && debug) window.console.profile("Create a detail .createDetailPage()");

            this.listeners.onCreateDetailPageStart.fire();

            var providerType = this.getProviderTypeByCode(params.providerTypeCode);

            // hide the search results.
            $('#canvas,.hiToolTip').addClass('invisible');

            // append the viewport then add events
            var v = providerType.detail.create().viewport;

            // get the detail attributes and send them along to the display function
            var detailAttributes = this.getDetailAttributes();

            // .remove() is destroying unrelated events for some reason
            var dw = document.getElementById('detailWrapper');
            if (dw && dw.parentNode) { dw.parentNode.removeChild(dw); };

            // create the detail
            var $d = $(document.createElement('div')).attr('id', 'detailWrapper').attr('class', 'invisible').append(v);
            $(Config.SiteTemplate.TemplateContainerId).append($d);
            $d.find('#detailNavContainer').bind('click', { 'providerType': providerType, 'params': params, 'detailAttributes': detailAttributes }, _epd.createDetailContentBridge).end()
              .find('#backButton').bind('click', { 'type': _epd.searchInfo.lastSearchParameters.providerType }, _epd.closeDetailPageBridge).end()
              .find('a#modalPopupDetails').bind('click', { 'that': _epd }, function() { MV.Modal.show('mvModalNeed') }).end()
              .find('a#qualityModalPopup').bind('click', { 'that': _epd }, function() { MV.Modal.show('mvModalLearn') }).end()
              .find('.printDetailLabel').unbind('click').bind('click', { 'params': params, 'detailAttributes': detailAttributes }, _epd.printAllDetail).end();

            // when creating the detail for the first time, automatically fire the default control
            var defaultid = (params.controlid) ? params.controlid : providerType.detail.controls.defaultControl;
            var eventData = {
                data: {
                    params: params,
                    window: providerType.detail.contentSections[defaultid],
                    viewport: providerType.detail.viewport,
                    detailAttributes: detailAttributes
                }
            }

            // fire the first nav item
            providerType.detail.controls[defaultid].listeners.onControlClick.fire(eventData);

            // handle selection of nav item
            $('#' + defaultid).parent().addClass('selected');

            // resize the viewport to handle the new section header height
            this.resizeApplicationViewportHelper('articleDetail');

            var listenerBundle = {
                planId: this.getPlanId()
            }

            // fire the listener
            this.listeners.onCreateDetailPageComplete.fire(listenerBundle);

            if (!$.browser.msie && debug) window.console.profileEnd();
        },

        createDetailContentBridge: function(e) {
            e.preventDefault();
            _epd.createDetailContent(e.target.id, e.data);
        },

        createDetailContent: function(targetId, data) {
            _epd.listeners.onCreateDetailContentStart.fire();
            var params = data.params;
            var fullID = targetId;
            var providerType = data.providerType;
            var detailAttributes = data.detailAttributes;
            var templates = providerType.detail.contentSections;
            var eventData = {
                data: {
                    params: params,
                    window: templates[fullID],
                    viewport: providerType.detail.viewport,
                    detailAttributes: detailAttributes
                }
            }

            if (!providerType.detail.controls[targetId]) { return false; }

            _epd.components.validation.reset();

            providerType.detail.clearContent();

            // change the selected nav item
            $('#detailNavContainer .selected').removeClass('selected');

            $('#' + targetId).parent().addClass('selected');

            providerType.detail.controls[targetId].listeners.onControlClick.fire(eventData);
            _epd.listeners.onCreateDetailContentComplete.fire();
        },

        closeDetailPageBridge: function(e) {
            e.preventDefault();
            _epd.closeDetailPage(e.data.type);
        },

        closeDetailPage: function(type) {
            _epd.listeners.onCloseDetailPageStart.fire();
            var providerType = _epd.getProviderTypeByCode(type);

            // .remove() is destroying unrelated events for some reason
            var dw = document.getElementById('detailWrapper');
            dw.parentNode.removeChild(dw);

            //  close it, just in case the user did not close it.
            $('.mvModalContainer').hide();

            // show the result set
            $('#canvas,.hiToolTip').removeClass('invisible');
            $('#selectedProviderTypeLink').html(providerType.name);

            var listenerBundle = {};

            // resize the directory listings
            _epd.resizeApplicationViewportHelper('directoryListingSection');
            _epd.listeners.onCloseDetailPageComplete.fire(listenerBundle);
        },

        writeDetailContent: function(providerTypeCode, title, body) {
            this.listeners.onWriteDetailContentStart.fire();
            this.getProviderTypeByCode(providerTypeCode).detail.writeContent(title, body);
            this.listeners.onWriteDetailContentComplete.fire();
        },

        printDetailContent: function(providerTypeCode, title, body) {
            this.listeners.onPrintDetailContentStart.fire();
            this.getProviderTypeByCode(providerTypeCode).detail.printContent(title, body);
            this.listeners.onPrintDetailContentComplete.fire();
        },

        printAllDetail: function(e) {
            var params = e.data.params;
            $("#printDetail").empty();
            MV.Util.BlockUI();
            _epd.getProviderTypeByCode(params.providerTypeCode).detail.printAll(e);
            window.setTimeout(function() { MV.Util.UnBlockUI(); window.print() }, 1000);
        },

        showDetailPage: function() {
            this.listeners.onShowDetailPageStart.fire();
            $('#detailWrapper').removeClass('invisible');
            MV.WebServices.Strings.BindFragment($('#detailWrapper'));
            MV.EPD.resizeApplicationViewportHelper('articleDetail');
            this.listeners.onShowDetailPageComplete.fire();
        },


        //** SEARCH RESULTS **//

        submitSearchForm: function(e) {
            e.preventDefault();
            this.listeners.onSubmitSearchFormStart.fire();
            var activeProviderType = _epd.getActiveProviderType();
            var typeid = activeProviderType.id;
            var pass = false;
            var listenerBundle = {
                providerType: activeProviderType
            }

            _epd.allowWSRender('NationalProviderSearch'); // allow WS rendering
            _epd.allowWSRender('ProviderSearch'); // allow WS rendering

            // check the navigation matrix to see if we need to do any url redirection
            if (_epd.redirectApplication()) { return false; };

            // when doing a search, remove the landing page
            // if there is an opening image, remove it.  todo: this needs to be handled better
            var directoryListingSection = $('#directoryListingSection');
            directoryListingSection.find('.welcomeText').addClass('invisible');

            var decoration = directoryListingSection.find('.decoration');
            decoration.addClass('invisible');
            decoration.removeClass('decorationWithForm');

            // fire the automated validation
            pass = _epd.components.validation.validate();

            // fire the common search form validation - do not allow for multiple validations for now
            pass = _epd.listeners.onSearchFormValidate.fns.length > 0 ? _epd.listeners.onSearchFormValidate.fns[0]() : pass;

            // only fire the first validation for provider type - do not allow for multiple validations for now
            pass = _epd.providerTypes[typeid].listeners.onValidate.fns.length > 0 ? _epd.providerTypes[typeid].listeners.onValidate.fns[0]() : pass;

            if (pass) {
                _epd.providerTypes[typeid].listeners.onSubmit.fire();
                _epd.searchInfo.isNewSearch = true;
                _epd.createSearchResults();
            }

            this.listeners.onSubmitSearchFormComplete.fire();
        },

        loadSearchParameters: function() {

            // make sure the listener exists
            if (!this.listeners || !this.listeners.onLoadSearchParametersStart
                                || typeof this.listeners.onLoadSearchParametersStart.fns[0] != 'function') {
                alert('onLoadSearchParametersStart has no definition.')
            }

            // row count is changed when show all is clicked.  on a new search we use the initialResultCount propery
            this.setReturnRowCount((!this.searchInfo.returnMaxResults) ? this.searchInfo.initialResultCount : this.searchInfo.fullResultCount);

            var wsparams = typeof this.listeners.onLoadSearchParametersStart.fns[0] == 'function' ? this.listeners.onLoadSearchParametersStart.fns[0]({ isNationalSearch: this.getActiveProviderType().config.National }) : {};

            // Use the parameters from the last search if we're missing some uber-required values.
            // (Probably because the user opened the provider type list and blew away the search form)
            // create app property
            if (!wsparams.network && !wsparams.zip && !wsparams.prefix && !wsparams.product && this.searchInfo.lastSearchParameters) {
                wsparams = this.searchInfo.lastSearchParameters;
            }

            wsparams.providerType = this.getActiveProviderTypeCode();
            wsparams.plan = this.getPlanId();
            wsparams.maxRows = this.getReturnRowCount();
            wsparams.sortBy = (this.searchInfo.lastSort) ? this.searchInfo.lastSort : this.searchInfo.defaultSort;

            // add the parameters to the instance of epd object for reference later
            this.searchInfo.lastSearchParameters = wsparams;

            this.listeners.onLoadSearchParametersComplete.fire();

            return wsparams;
        },

        createSearchResults: function() {
            this.listeners.onCreateSearchResultsStart.fire();
            var wsparams = this.loadSearchParameters();
            //prevent ShowAll and healthinfo tab over lapped...
            //unbind the event - mousedown 
            if (!$('#healthInfoTab').hasClass('invisible')) {
                $('#healthInfoTab').unbind('mousedown');
            }

            $('.printLink').addClass('invisible');

            if (this.searchInfo.isNewSearch) {
                this.setRowCountDisplay(this.searchInfo.initialResultCount);
                this.setReturnRowCount(this.searchInfo.fullResultCount);
                this.searchInfo.lastSort = this.searchInfo.defaultSort;
            } else {
                this.setReturnRowCount(this.searchInfo.fullResultCount);
                this.setRowCountDisplay(this.searchInfo.fullResultCount);
            }
            MV.Util.BlockUI();
            var providerType = this.getProviderTypeByCode(wsparams.providerType);
            var isNational = providerType.config.National;
            this.searchInfo.isNationalSearch = isNational;

            MV.WebServices.EPD.GetProfileAttributesWrapper(isNational, wsparams.plan, wsparams.providerType, wsparams.specialtyCategoriesType, wsparams.network, function(data) {
                if (!isSecurePath()) {	//fix non-secure searches.  secure is not a key in the matrix
                    data.ResultColumns = data.ResultColumns.replace(',column6', '');
                }
                var profile = _epd.getResultColumnProfile(data.ResultColumns);
                _epd.components.dataTable.settings.resultCols = data.ResultColumns;
                _epd.components.dataTable.settings.resultColsProfile = profile;

                _epd.components.detail.create(providerType, data);

                var prefixSearch = (wsparams.prefix != '')

                var callSearchWS = function() {
                    var formattedParams = formatWSParameters(wsparams);
                    MV.WebServices.EPD.ProviderSearchWrapper(isNational, formattedParams, function(data) {
                        _epd.createSearchResultsContent(data, profile);
                        _epd.listeners.onProviderSearchEvent.fire({ isNationalSearch: isNational, searchparams: wsparams });
                        MV.Util.UnBlockUI();

                        //prevent ShowAll and healthinfo tab over lapped...
                        //bind the event - mousedown 
                        if (!$('#healthInfoTab').hasClass('invisible')) {
                            $('#healthInfoTab').bind('mousedown', _epd.showHealthInfoPage);
                        }

                        var printLink = $('.printLink').attr('href', '#');
                        MV.WebServices.EPD.CreatePDFForSearchWrapper(isNational, formattedParams, function(data) {
                            printLink.removeClass('invisible').attr('href', data).click(function() { MV.EPD.openWin({ url: data, label: $(this).text() }); return false; });
                        });
                    });
                }

                if (isNational && prefixSearch) {
                    MV.WebServices.EPD.ValidatePrefix(['Prefix', wsparams.prefix || '999999'], function(success, results) {
                        _epd.clearValidationError('memberPrefixInput');
                        if (!success && isNational && prefixSearch) {
                            _epd.forceValidationError('memberPrefixInput', MV.WebServices.Strings.GetEntry('memberPrefixInvalidError'));
                            MV.Util.UnBlockUI();
                        } else {
                            callSearchWS();
                        }
                    });
                } else {
                    callSearchWS();
                }
            });

            // Load profile attributes for the provider's group, if available.
            if (providerType.config.groupCode) {
                MV.WebServices.EPD.GetProfileAttributes(wsparams.plan, providerType.config.groupCode, null, null, function(data) {
                    _epd.components.detail.create(_epd.getProviderTypeByCode(providerType.config.groupCode), data);
                });
            }

            this.listeners.onCreateSearchResultsComplete.fire({ isNationalSearch: isNational, searchparams: wsparams });
        },

        createAllSearchResults: function(e) {
            e.preventDefault();
            _epd.searchInfo.isNewSearch = false;
            _epd.createSearchResults();
        },

        createSearchResultsContent: function(data, profile) {
            if (!$.browser.msie && debug) window.console.profile("Create the search results .createSearchResults()");

            if (!this.canRenderWSRequest('ProviderSearch') && !this.canRenderWSRequest('NationalProviderSearch')) {
                return;
            };

            this.listeners.onCreateSearchResultsContentStart.fire();

            var sortBy = this.searchInfo.lastSort;
            var wsparams = this.searchInfo.lastSearchParameters;
            var resultColumnProfile = profile || _epd.components.dataTable.settings.resultColsProfile;

            this.searchInfo.lastSearchResults = data;
            if (data && data.length > 0 && data[0]) {
                this.searchInfo.lastSearchLatLng = { lat: data[0].Latitude, lng: data[0].Longitude };
            }

            // if we are not doing a new search, ie doing a show all after we have sorted.  resort the data
            // returned from the web service.
            if (!this.searchInfo.isNewSearch) {
                data.sort(this.sorters[sortBy])
            }

            // set the sort icon
            $('#' + sortBy + 'MainHeader').siblings().removeClass('sorted ascending').end().addClass('sorted ascending');

            // figure out how many results to show
            var displayData = data.slice(0, this.getRowCountDisplay());

            // set up the results header and summary
            $('#directoryListingTab').removeClass('invisible');

            if (this.searchInfo.isNewSearch) {
                if (wsparams != undefined)
                    this.loadHealthInfoContent(wsparams);
            }

            $('#providerResultHeadTable').removeAttr('class').addClass(resultColumnProfile);

            var displayRows = 0, totalRows = 0;
            if (data && data.length > 0 && data[0]) {
                displayRows = displayData.length;
                totalRows = data.length;

                $('.printLink').unbind('click').click(function(e) {
                    e.preventDefault();
                    var url = $(e.target).attr('href');
                    if (url && url != '#') {
                        MV.EPD.openWin({ url: url, label: $(this).text() });
                    } else {
                        window.print();
                    }
                });
            } else {
                $('.printLink').addClass('invisible');
            }

            $('#displayRows').html(displayRows);
            $('#totalRows').html(totalRows);

            if (displayData.length >= data.length) { $('#showAllResults').addClass('invisible') } else { $('#showAllResults').removeClass('invisible').bind('click', _epd.createAllSearchResults) };
            $('#resultsSummary').removeClass('invisible');

            var dtSettings = this.components.dataTable.settings;
            var tblString = this.components.dataTable.createResults(displayData, dtSettings.dataTransform, dtSettings.resultCols, resultColumnProfile, this.searchInfo.isNationalSearch);

            $('#providerResultContainer').get(0).innerHTML = tblString;
            $('#providerResultContainer').bind('click', _epd.delegateSearchResultsEvents);
            $('#directoryListingSection').removeClass('invisible');

            if (!$.browser.msie && debug) window.console.profileEnd();

            // resize the viewport to handle the new section header height
            this.resizeApplicationViewportHelper('directoryListingSection');

            this.listeners.onCreateSearchResultsContentComplete.fire();

        },

        sortSearchResults: function(sortKey) {
            this.listeners.onSortSearchResultsStart.fire();

            _epd.allowWSRender('NationalProviderSearch'); // allow WS rendering
            _epd.allowWSRender('ProviderSearch'); // allow WS rendering

            if (this.searchInfo.lastSearchResults.length === 0) return false;

            // if it is the initial set, go back to the web service
            if (!this.searchInfo.returnMaxResults) {
                this.searchInfo.lastSort = sortKey;
                this.searchInfo.isNewSearch = false;
                return this.createAllSearchResults();
            }

            if (this.sorters[sortKey] && this.sorters[sortKey] !== this.searchInfo.lastSort) {
                this.searchInfo.lastSort = sortKey;
                this.searchInfo.isNewSearch = false;
                this.createSearchResultsContent(this.searchInfo.lastSearchResults.sort(this.sorters[sortKey]));
            }
            this.listeners.onSortSearchResultsComplete.fire();
        },

        delegateSearchResultsEvents: function(e) {
            e.preventDefault();
            var $ele = $(e.target);

            if ($ele.length > 0) {
                var idArr = $ele.attr('id').split('-');
                var eventDestination = idArr[1];
                var providerId = idArr[0];
                var groupId = $ele.attr("groupid");
                var recordId = $ele.attr("recordid");
                var groupRecordId = $ele.attr("officeRecordID");
                var productCode = $ele.attr("productcode");
                var lastSearchParameters = _epd.searchInfo.lastSearchParameters;

                var planId = lastSearchParameters.plan;
                var providerCode = lastSearchParameters.providerType;
                var specialtyCode = lastSearchParameters.specialtyCategoriesType;
                var networkId = lastSearchParameters.network;

                var providerType = _epd.getActiveProviderType();
                var isNational = providerType.config.National;

                switch (eventDestination) {
                    case 'viewdetail':

                        MV.WebServices.EPD.GetProfileAttributesWrapper(isNational, planId, providerCode, specialtyCode, networkId, function(data) {
                            _epd.components.detail.create(_epd.getActiveProviderType(), data);
                            var params = {
                                providerId: providerId,
                                providerTypeCode: providerCode,
                                specialtyCode: specialtyCode,
                                planId: planId,
                                networkId: networkId,
                                latLng: _epd.searchInfo.lastSearchLatLng,
                                groupId: groupId,
                                recordId: recordId,
                                prefix: lastSearchParameters.prefix,
                                product: isNational ? productCode : lastSearchParameters.product
                            };
                            _epd.createDetailPage(params);
                            _epd.listeners.onShowDetailPageComplete.fire(params);
                        });
                        break;

                    case 'viewofficedetail':
                        var providerTypeCode = providerType.providerTypeCode;
                        var groupType = providerType;
                        var groupCode = providerTypeCode;
                        var productCode = $ele.attr("productcode");


                        if (providerType.config.groupCode) {
                            groupCode = providerType.config.groupCode;
                            groupType = _epd.getProviderTypeByCode(groupCode);
                            specialtyCode = null;
                            if (!providerType.config.isPrimaryTypeForGroup) {
                                networkId = null;
                            }
                        }

                        MV.WebServices.EPD.GetProfileAttributesWrapper(isNational, planId, groupCode, specialtyCode, networkId, function(data) {
                            _epd.components.detail.create(groupType, data);
                            var params = {
                                providerId: providerId,
                                providerTypeCode: groupCode,
                                specialtyCode: specialtyCode,
                                planId: planId,
                                networkId: networkId,
                                latLng: _epd.searchInfo.lastSearchLatLng,
                                groupId: groupId,
                                recordId: groupRecordId,
                                controlid: 'office-profile',
                                prefix: lastSearchParameters.prefix,
                                product: isNational ? productCode : lastSearchParameters.product
                            };
                            _epd.createDetailPage(params);
                            _epd.listeners.onShowDetailPageComplete.fire(params);
                        });
                        break;
                    case 'maplocation':
                        //window.open(unescape($ele.attr('mv')), '', 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes, resizable=yes');
                        MV.EPD.openWin({ url: $ele.attr('mv'), label: $ele.text() }, '', 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes, resizable=yes');
                        break;
                    case 'redirecthospital':
                        $('#selectedProviderTypeTitle').click();
                        _epd.showSearchForm('hospital');
                        break;
                }
            }
        },


        //** HEALTH INFO TAB **//

        loadHealthInfoContent: function(wsparams) {
            this.listeners.onLoadHealthInfoContentStart.fire();
            this.searchInfo.isHealthIssueSearch = false;
            this.hideHealthInfoPage();

            if (wsparams.condProcSurgId && wsparams.condProcSurgId != '0000') {
                this.searchInfo.isHealthIssueSearch = true;
            }

            if (this.searchInfo.isHealthIssueSearch) {
                var str = wsparams.condProcSurgId.split('-');
                var id = str[0];
                var what = str[1];
                var method = (what == 'condition') ? 'GetConditionReference' : 'GetProcedureReference';
                MV.WebServices.EPD[method](id, function(data) {
                    _epd.createHealthInfoPage(data);
                });
            }
            this.listeners.onLoadHealthInfoContentComplete.fire();
        },

        hideHealthInfoPage: function() {
            this.listeners.onHideHealthInfoPageStart.fire();
            $("#healthInfoTab").removeClass('active').addClass('invisible');
            $('#healthInfoContentSection').addClass('invisible');
            $("#directoryListingTab").addClass('active');
            $("#healthInfoLinkContainer").addClass('invisible');
            this.listeners.onHideHealthInfoPageComplete.fire();
        },

        showSearchResults: function() {
            _epd.listeners.onShowSearchResultsStart.fire();
            $('#healthInfoContentSection').addClass('invisible');
            $('#directoryListingSection').removeClass('invisible');
            $('#healthInfoTab').removeClass('active');
            $('#directoryListingTab').addClass('active');

            _epd.resizeApplicationViewportHelper('directoryListingSection');
            _epd.listeners.onShowSearchResultsComplete.fire();
        },

        showHealthInfoPage: function() {
            _epd.listeners.onShowHealthInfoPageStart.fire();
            $('#healthInfoContentSection').removeClass('invisible');
            $('#directoryListingSection').addClass('invisible');
            $('#healthInfoTab').addClass('active');
            $('#directoryListingTab').removeClass('active');

            _epd.resizeApplicationViewportHelper('healthInfoContentSection');
            _epd.listeners.onShowHealthInfoPageComplete.fire();
        },

        createHealthInfoPage: function(data) {
            this.listeners.onCreateHealthInfoPageStart.fire();
            if (data.Urls.length == 0 && data.Questions.length == 0) {
                return;
            }

            var table = this.templates.healthInfo.informationTable.clone(true);
            var tableHeader = table.find('thead.related').clone(true);
            var tableBody = table.find('tbody.related');
            var dataRow = this.templates.healthInfo.informationDataRow.clone(true);
            var currentDataRow;

            for (var a = 0; a < data.Urls.length; a += 1) {
                currentDataRow = dataRow.clone();
                currentDataRow.find('.healthLink').html(data.Urls[a].Name);
                var elm = currentDataRow.find('.healthLink').attr('href', data.Urls[a].Url);
				elm.get(0).onclick = 
                    function(dataItem) {
                        return function() {
                            MV.EPD.openHealthwiseWin(dataItem.Name, dataItem.Url); return false;
                        }
                    } (data.Urls[a]);

                tableBody.append(currentDataRow);
            }

            table.find('#relatedHealthInfoToggle').click(function() {
                tableBody.toggleClass('invisible');
                $('#relatedHealthInfoToggle').toggleClass('expanded');
            });

            var tableQ = this.templates.healthInfo.questionTable.clone(true);
            var tableHeaderQ = tableQ.find('thead.question').clone(true);
            var tableBodyQ = tableQ.find('tbody.question');
            var dataRowQ = this.templates.healthInfo.questionDataRow.clone(true);

            for (var a = 0; a < data.Questions.length; a += 1) {
                currentDataRow = dataRowQ.clone();
                currentDataRow.find('.questionItem').html(data.Questions[a]);
                tableBodyQ.append(currentDataRow);
            }

            tableQ.find('#relatedHealthQuestionToggle').click(function() {
                tableBodyQ.toggleClass('invisible');
                $('#relatedHealthQuestionToggle').toggleClass('expanded');
            });


            var healthSection = this.templates.healthInfo.section.clone(true)
                .find('#healthInfoContentSection')
                .height(_epd.applicationSettings.viewport.sectionHeight)
                .find('.wrapper').height(_epd.applicationSettings.viewport.wrapperHeight)
                .append(table).append(tableQ).end();

            // .remove() is destroying unrelated events for some reason
            var hs = document.getElementById('healthInfoContentSection');
            if (hs && hs.parentNode) { hs.parentNode.removeChild(hs); };

            $('#content').prepend(healthSection)

            if ($('#healthInfoTab').hasClass('active') === true) {
                $('#healthInfoContentSection').removeClass('invisible');
            }

            var sh = '';
            var shDesc = '';
            if (data.Condition != undefined) {
                sh = data.Condition.ConsumerTerm;
                shDesc = data.Condition.Description;
            }
            else {
                sh = data.Procedure.ConsumerTerm;
                shDesc = data.Procedure.Description;
            }

            $('#sectionHeader,#cspName').html(sh);
            $('#sectionDescription').html(shDesc);

            if (data.Urls.length == 0) $('#relatedHealthInfoToggle').addClass('invisible');
            if (data.Questions.length == 0) $('#relatedHealthQuestionToggle').addClass('invisible');

            $('#healthInfoTab').removeClass('invisible').bind('mousedown', _epd.showHealthInfoPage);
            $("#healthInfoLinkContainer").removeClass('invisible').bind('mousedown', _epd.showHealthInfoPage);
            $('#directoryListingTab').bind('mousedown', _epd.showSearchResults);

            this.listeners.onCreateHealthInfoPageComplete.fire();
        },


        //** APPLICATION UTILITY METHODS **/

        resetApplication: function() {
            this.listeners.onResetApplicationStart.fire();
            var _reset = function() {
                _epd.components.validation.reset();
                _epd.components.navigation.reset();
                $('#providerTypeForm').addClass('invisible').find('div.providerTypeFieldsetContainer').remove();
            }

            // if there is a health issue autocomplete we need to add a 200 ms delay so the autocomplete plugin can be removed
            // before the form element input is removed
            var h = $('#healthIssueInput');
            if (h.length > 0) {
                h.blur();
                setTimeout(_reset, 200)
            } else {
                _reset();
            }
            this.listeners.onResetApplicationComplete.fire();
        },

        resizeApplicationViewport: function(viewportid) {
            this.listeners.onResizeApplicationViewportStart.fire();
            var version = parseInt(jQuery.browser.version, 10);
            if ($.browser.msie) {
                if (version == 7) {
                    if ($(window).width() != this.applicationSettings.window.previous.width) {
                        this.applicationSettings.window.previous.width = $(window).width();
                    } else {
                        return false;
                    }
                }
            }
            this.resetApplicationDimensions();
            this.resizeApplicationViewportHelper(viewportid);
            this.listeners.onResizeApplicationViewportComplete.fire();
        },

        resizeApplicationViewportHelper: function(viewportid) {
            if (!$.browser.msie && debug) window.console.profile("Resize the viewport .resizeApplicationViewportHelper()");

            var context = $('#detailWrapper');
            if (!context[0])
                context = $('#canvas');

            var sh = resizeApplicationSection(context);
            var wh = resizeApplicationWrapper(context);

            if (viewportid) {
                this.setApplicationDimensions(viewportid, sh, wh);
            }

            if (!$.browser.msie && debug) window.console.profileEnd();
        },

        setApplicationDimensions: function(viewportid, sectionheight, wrapperheight) {
            this.applicationSettings.viewport[viewportid].sectionHeight = sectionheight;
            this.applicationSettings.viewport[viewportid].wrapperHeight = wrapperheight;
        },

        resetApplicationDimensions: function() {
            this.applicationSettings.viewport.directoryListingSection = {};
            this.applicationSettings.viewport.healthInfoContentSection = {};
        },

        //** REGISTER FUNCTIONS / ADDER'S / GETTER'S / SETTER'S **//

        registerDataTableSettings: function(defaultDataTableConfig) {
            this.components.dataTable.settings = defaultDataTableConfig;
        },

        registerValidationRules: function(validationRules) {
            this.components.validation.rules = validationRules;
        },

        registerDetailSettings: function(defaultDetailConfig) {
            this.components.detail.settings = defaultDetailConfig;
        },

        registerProviderType: function(typeid, opts) {
            for (var o in opts) {
                // for convenience of adding onLoad to a detail
                if (o == 'onDetailLoad') {
                    this.addProviderTypeDetailListener('onLoad', typeid, opts[o]);
                    continue;
                }

                // for convenience of adding onDetailPrint to a detail
                if (o == 'onDetailPrint') {
                    this.addProviderTypeDetailListener('onPrint', typeid, opts[o]);
                    continue;
                }

                // overloading this to register details controls
                if (o == 'detailControls') {
                    for (var detailControl in opts[o]) {
                        this.registerDetailControl(typeid, opts[o][detailControl].controlID, opts[o][detailControl].func);
                    }
                    continue;
                }


                this.addProviderTypeListener(o, typeid, opts[o]);
            }
            return this;
        },

        registerDetailControl: function(typeid, menuid, onControlClickFn) {
            this.getProviderTypeById(typeid).detail.controls[menuid].listeners.onControlClick.subscribe(onControlClickFn);
            return this;
        },

        addSorters: function(obj) {
            this.sorters = $.extend(this.sorters, obj)
            this.addSorterEvents();
        },

        addSorter: function(id, fn) {
            this.sorters[id] = fn;
            this.addSorterEvent(id);
        },

        addSorterEvents: function() {
            for (var sorter in this.sorters) {
                _epd.addSorterEvent(sorter);
            };
        },

        addSorterEvent: function(sortKey) {
            $('#' + sortKey + 'MainHeader').addClass('sortable');

            if (!$.data(this, 'MV.sortKey')) $.data(this, 'MV.sortKey', sortKey);

            $('#' + sortKey + 'MainHeader').mousedown(function(e) {
                e.preventDefault();
                var classNames = $(this).attr('class');
                if (!classNames.match('sorted')) {
                    $(this).siblings().removeClass('sorted ascending');
                    $(this).addClass('sorted ascending');
                    _epd.sortSearchResults(sortKey);
                }
            });
        },

        addListeners: function(objarray) {
            if (!objarray) return;
            var id, fn, obj;
            for (var i = 0; i < objarray.length; i++) {
                for (obj in objarray[i]) {
                    id = obj;
                    fn = objarray[i][obj];
                };
                _epd.addListener(id, fn);
            };
        },

        addListener: function(id, fn) {
            if (!id || !fn) return;
            if (!this.listeners[id]) return alert('"' + id + '" is not a supported listener for EPD');
            this.listeners[id].subscribe(fn);
        },

        addDetailListener: function(listener, fn) {
            if (!this.providerTypeDetail.listeners[listener]) return alert('"' + listener + '" is not a supported listener for EPD Detail');
            this.providerTypeDetail.addListener(listener, fn);
        },

        addProviderTypeListener: function(listener, typeid, fn) {
            if (!this.providerTypes[typeid]) return alert('"' + typeid + '" is not a valid provider type but has a definition in provider_search');
            this.providerTypes[typeid].listeners[listener].subscribe(fn);
        },

        addProviderTypeDetailListener: function(listener, typeid, fn) {
            if (!this.providerTypes[typeid]) return alert('"' + typeid + '" is not a valid provider type');
            if (!this.providerTypes[typeid].detail.listeners[listener]) return alert('"' + listener + '" is not a valid provider type detail listener');
            this.providerTypes[typeid].detail.listeners[listener].subscribe(fn);
        },

        addProviderTypeValidation: function(typeid, fn) {
            var typearray = typeid.split(' ');
            for (var i = 0; i < typearray.length; i++) {
                var type = typearray[i];
                _epd.addProviderTypeListener('onValidate', type, fn);
            };
        },

        addProviderTypeOnShow: function(typeid, fn) {
            var typearray = typeid.split(' ');
            for (var i = 0; i < typearray.length; i++) {
                var type = typearray[i];
                _epd.addProviderTypeListener('onShow', type, fn);
            };
        },

        getSearchFormRedirectUrl: function(clientProviderTypeCode, clientNetworkId, clientSubtypeId) {

            var h = this.applicationSettings.clientNavigationHash;
            var p = this.getApplicationMatrixLookupParameters(clientProviderTypeCode, clientNetworkId, clientSubtypeId);
            if (h && h[p.clientProviderTypeCode] && h[p.clientProviderTypeCode][p.clientNetworkId] && h[p.clientProviderTypeCode][p.clientNetworkId][p.clientSubtypeId]) {
                if (h[p.clientProviderTypeCode][p.clientNetworkId][p.clientSubtypeId].redirectUrl) {
                    return {url: h[p.clientProviderTypeCode][p.clientNetworkId][p.clientSubtypeId].redirectUrl, label: p.clientProviderTypeCode+'|'+p.clientNetworkId+'|'+p.clientSubtypeId};
                }
            } else {
                return false;
            }
        },

        getDetailAttributes: function(clientProviderTypeCode, clientNetworkId, clientSubtypeId) {
            var h = this.getDetailAttributeHash();
            var p = this.getApplicationMatrixLookupParameters(clientProviderTypeCode, clientNetworkId, clientSubtypeId);
            if (h && h[p.clientProviderTypeCode] && h[p.clientProviderTypeCode][p.clientNetworkId] && h[p.clientProviderTypeCode][p.clientNetworkId][p.clientSubtypeId]) {
                return h[p.clientProviderTypeCode][p.clientNetworkId][p.clientSubtypeId];
            } else {
                return false;
            }
        },

        getApplicationMatrixLookupParameters: function(clientProviderTypeCode, clientNetworkId, clientSubtypeId) {
            // check validation matrix against loadedParams to determine URL redirection
            var networkSelect = ($('#networkInput').length == 0) ? '' : $('#networkInput');
            var specialtySelect = ($('#specialtyInput').length == 0) ? '' : $('#specialtyInput');
            var networkSelectVal = (!networkSelect || networkSelect.val() == 0 || networkSelect.val() == '') ? 'EMPTY' : networkSelect.val();
            var specialtySelectVal = (!specialtySelect || specialtySelect.val() == 0 || specialtySelect.val() == '') ? 'EMPTY' : specialtySelect.val();
            clientProviderTypeCode = clientProviderTypeCode || this.getActiveProviderType().providerTypeCode || 'EMPTY';
            clientNetworkId = clientNetworkId || networkSelectVal || 'EMPTY';
            clientSubtypeId = clientSubtypeId || specialtySelectVal || 'EMPTY';

            return {
                'clientProviderTypeCode': clientProviderTypeCode,
                'clientNetworkId': clientNetworkId,
                'clientSubtypeId': clientSubtypeId
            }
        },

        setDetailAttributeHash: function(hashobj) {
            this.applicationSettings.clientDetailAttributesHash = hashobj;
        },

        getDetailAttributeHash: function(hashobj) {
            return this.applicationSettings.clientDetailAttributesHash;
        },

        getProviderTypeByCode: function(typeCode) {
            var providerType;
            for (var x in this.providerTypes) {
                if (this.providerTypes[x].providerTypeCode == typeCode) {
                    providerType = this.providerTypes[x];
                    break;
                }
            }
            return providerType;
        },

        getProviderTypeById: function(typeid) {
            return this.providerTypes[typeid] || '';
        },

        setActiveProviderType: function(providerType) {
            return this.activeProviderType = providerType;
        },

        getActiveProviderType: function() {
            return this.activeProviderType;
        },

        getActiveProviderTypeCode: function() {
            return this.getActiveProviderType().providerTypeCode || '';
        },

        setReturnRowCount: function(rowcount) {
            this.searchInfo.returnRowCount = rowcount;
        },

        getReturnRowCount: function() {
            return this.searchInfo.returnRowCount;
        },

        setPlanId: function(planId) {
            this.applicationSettings.planId = planId;
        },

        getPlanId: function() {
            return this.applicationSettings.planId;
        },

        setNetworkId: function(networkId) {
            this.searchInfo.lastSearchParameters.network = networkId;
        },

        getNetworkId: function() {
            return this.searchInfo.lastSearchParameters.network;
        },

        setLatLng: function(latLng) {
            this.searchInfo.lastSearchLatLng = latLng
        },

        getLatLng: function() {
            return this.searchInfo.lastSearchLatLng;
        },

        setRowCountDisplay: function(rowcount) {
            this.searchInfo.displayRowCount = rowcount;
        },

        getRowCountDisplay: function() {
            return this.searchInfo.displayRowCount;
        },

        setStandardSearchFormDropdownData: function(data) {
            this.applicationSettings.standardSearchDropdownData = data;
        },

        getStandardSearchFormDropdownData: function() {
            return this.applicationSettings.standardSearchDropdownData;
        },

        setResultColumnProfile: function(id, value) {
            resultColumnProfileHash[id] = value;
        },

        getResultColumnProfile: function(id) {
            return resultColumnProfileHash[id] || 'other';
        },

        isNationalAlliance: function() {
            return !(this.applicationSettings.referrer === 'SC' ||
                    this.applicationSettings.referrer === 'SCS' ||
                    this.applicationSettings.referrer === 'SCPS' ||
                    this.applicationSettings.referrer === 'BC' ||
                    this.applicationSettings.referrer === 'BCS' ||
                    this.applicationSettings.referrer === 'BCPS' ||
                    this.applicationSettings.referrer === 'DU');

        },

        isNational: function() {
            return this.getActiveProviderType().config.National;
        },

        allowWSRender: function(WSName) {
            this.applicationSettings.wsRenderHash[WSName] = true;
        },

        cancelWSRender: function(WSName) {
            this.cancelCurrentAjaxRequest();
            this.applicationSettings.wsRenderHash[WSName] = false;
            if (MV.Util.UnBlockUI) { MV.Util.UnBlockUI(); }
        },
        cancelCurrentAjaxRequest: function() {
            if (mv_ws_current_request) { mv_ws_current_request.abort(); }
        },
        canRenderWSRequest: function(WSName) {
            var d = (typeof this.applicationSettings.wsRenderHash[WSName] !== 'boolean') ? true : this.applicationSettings.wsRenderHash[WSName];
            return d;
        },

        openHealthwiseWin: function(consumerTerm, url, name, params) {
            if (url) {
				if (!params){
					params = 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes, resizable=yes';
				}
                var offsiteWindow = window.open(url, name, params);
                offsiteWindow.focus();
            }
            this.listeners.onHealthwiseLinkClicked.fire({ url: url, consumerTerm: consumerTerm });
            return false;
        },

        openWin: function(linkObj, name, params, noTrack) {
            if (typeof linkObj === 'string') {
                linkObj = {url: linkObj, label: ''}
            }
            var url = linkObj.url;
            if (url) {
				if (!params){
					params = 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes, resizable=yes';
				}
                var offsiteWindow = window.open(url, name, params);
                offsiteWindow.focus();
            }

            if (noTrack !== true) {
                this.listeners.onExternalLinkClicked.fire(linkObj);
            }

            return false;
        }

        // module end.
    };

})();
