// Copyright 2012-2023 Ellucian Company L.P. and its affiliates.

var Ellucian = Ellucian || {};
Ellucian.ScreenSizes = Ellucian.ScreenSizes || {};
Ellucian.ScreenSizes = Object.freeze({ 'xxsmall': 320, 'xsmall': 420, 'small': 768, 'medium': 992, 'tablets': 1025, 'large': 1200, 'xlarge': 1440, 'xxlarge': 2000 });
var BaseViewModel = function (mobileScreenWidth) {

    var self = this;

    this.mobileScreenWidth = mobileScreenWidth || 480;

    this.isMobile = ko.observable(false);
    var screenSize = ko.observable(0);

    this.screenIsSmallerThan = function (size) {
        // Ensure provided size is one of the keys in the screen size dictionary (Ellucian.ScreenSizes)
        var valid = false;
        for (var key in Ellucian.ScreenSizes) {
            if (size === key) {
                valid = true;
                break;
            }
        }
        if (!valid) {
            throw "Please provide a valid screen query size from Ellucian.ScreenSizes";
        }

        var sizeValue = Ellucian.ScreenSizes[size];
        if (screenSize() < Ellucian.ScreenSizes[size]) {
            return true;
        }
        return false;
    }

    // Check the window, if it is mobile-sized, render appropriately.
    this.checkForMobile = function (win, doc) {
        // get the appropriate viewport width, so that JS and media queries trigger at same time
        // viewport script snippit from http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
        var windowValue = win, innerValue = 'inner';

        // determine whether to calculaute viewport using window.innerWidth or document.clientWidth (depends on browser)
        if (!('innerWidth' in win)) {
            innerValue = 'client';
            windowValue = doc.documentElement || doc.body;
        }

        // calculate viewport width
        var width = windowValue[innerValue + 'Width'];

        //checks viewport size on page load and adjusts page appropriately (copied/pasted from resize function)
        if (width < self.mobileScreenWidth) {
            self.isMobile(true);
        }
        else {
            self.isMobile(false);
        }
        screenSize(width);
    }

    // subscribe to isMobile to drive DOM changes
    this.isMobile.subscribe(function (newValue) {
        if (newValue === true) {
            self.changeToMobile();
        } else {
            self.changeToDesktop();
        }
    });

    this.changeToMobile = function () {
        // This method should be overridden in any class that uses BaseViewModel
    };

    this.changeToDesktop = function () {
        // This method should be overridden in any class that uses BaseViewModel
    };

    // Action throbber observables - used when loading proxy selection dialog
    self.loadingMessage = ko.observable(personProxyLoadingThrobberMessage);

    // Flag indicating whether or not the selection dialog is displayed
    self.proxySelectionDialogIsLoading = ko.observable(false);

    // Flag indicating whether or not the selection dialog is displayed
    self.proxySelectionDialogIsDisplayed = ko.observable(false);

    // List of available proxy subjects
    this.ProxySubjects = ko.observableArray();

    // Observable to keep track of the proxy subject that the user selects
    self.selectedProxySubject = ko.observable(null);

    // Function to process the Change User button to change the proxy subject
    self.changeProxySubject = function () {
        hideProxyDialog = false;
        self.getProxySubjects();
    }

    // listens to UPDATE.PROXY.BANNER to force the update of the stick binding for the proxy banner.
    self.updateProxyBanner = ko.observable(false).subscribeTo("UPDATE.PROXY.BANNER");

    // Updates the view model with proxy subject data
    self.updateViewModelWithProxySubjectData = function () {
        self.proxySelectionDialogIsLoading(false);
        self.proxySelectionDialogIsDisplayed(!hideProxyDialog && self.ProxySubjects().length > 1);
        showOrHideElement(self.ProxySubjects(), $("#changeProxyUser-link"));
        showOrHideElement(self.ProxySubjects(), $("#changeProxyUser-opsLink"))
        if (self.proxySelectionDialogIsDisplayed()) {
            setFocus("input.buttonless");
        } else {
            setFocus("#topOfForm");
        }
    }

    // Call to retrieve the user's available proxy subjects and build the proxy dialog JS view model
    this.getProxySubjects = function () {
        var proxySubjects = Ellucian.Storage.session.getItem("Ellucian.proxySubjects")
        if (!proxySubjects) {
            $.ajax({
                url: getProxySubjectsActionUrl,
                type: "GET",
                contentType: jsonContentType,
                dataType: "json",
                beforeSend: function (data) {
                    self.proxySelectionDialogIsLoading(true);
                },
                success: function (data) {
                    if (!account.handleInvalidSessionResponse(data)) {
                        Ellucian.Storage.session.setItem("Ellucian.proxySubjects", data.ProxySubjects)
                        $(document).trigger("proxySubjectsReady");
                        ko.mapping.fromJS(data, {}, self);
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    if (jqXHR.status != 0) {
                        $('#notificationHost').notificationCenter('addNotification', { message: proxySelectionUnableToLoadSubjectsMessage, type: "error" });
                    }
                },
                complete: function () {
                    self.updateViewModelWithProxySubjectData();
                }
            });
        } else {
            ko.mapping.fromJS(proxySubjects, {}, self.ProxySubjects);
            self.updateViewModelWithProxySubjectData();
        }
    }

    // Function to close the proxy subject modal dialog
    self.cancelProxySelectionDialog = function () {
        self.proxySelectionDialogIsDisplayed(false);
        self.selectedProxySubject(null);
    }

    // Action throbber observables - used when loading proxy selection dialog
    self.changingUserMessage = ko.observable(personProxyChangingThrobberMessage);

    // Flag indicating whether or not the selection dialog is displayed
    self.changingProxyUsers = ko.observable(false);

    // Function to process the user's proxy subject selection
    self.saveProxySelectionDialog = function () {

        // If the user selects not to proxy anyone and they just logged in, simply close the dialog
        var url = homeUrl;
        if (self.selectedProxySubject().Id() == currentUserId) {
            url += "?hideProxyDialog=true";
        }

        $.ajax({
            url: setProxySubjectActionUrl,
            type: "PUT",
            contentType: jsonContentType,
            data: ko.toJSON(self.selectedProxySubject()),
            dataType: "json",
            beforeSend: function (data) {
                self.changingProxyUsers(true);
            },
            success: function () { },
            error: function () {
                console.log("Error setting proxy subject.");
            },
            complete: function (data) {
                window.location.href = url;
            }
        });
    }

    // Observable to determine whether or not the submit button is enabled on the proxy selection dialog
    self.saveProxySelectionEnabled = ko.computed(function () {
        return self.selectedProxySubject();
    });

    // Announce the user's proxy subject selection
    self.selectedProxySubject.subscribe(function () {
        if (self.selectedProxySubject()) {
            makeAnnouncement(proxySelectionUserSelectedMessage.format(self.selectedProxySubject().FullName()));
        }
    });

    // Action throbber observables - used when verifying user's password
    self.verifyingPasswordMessage = ko.observable(verifyingPasswordMessage);
    self.verifyingPasswordAltText = ko.observable(verifyingPasswordAltText);

    // Flag indicating whether or not the user's password is being verified
    self.verifyingPassword = ko.observable(false);

    // Observable to store user's password for verification
    self.userPassword = ko.observable();

    // Observable to store password verification message
    self.verifyPasswordMessage = ko.observable();

    // Flag indicating whether or not the Verify Password dialog is displayed
    self.verifyPasswordDialogIsDisplayed = ko.observable(false);

    // Function to display verify password dialog
    self.showVerifyPasswordDialog = function () {
        self.verifyPasswordDialogIsDisplayed(true);
    }

    // Function to close the Verify Password modal dialog
    self.cancelVerifyPasswordDialog = function () {
        self.verifyPasswordDialogIsDisplayed(false);
        self.verifyPasswordMessage(null);
        self.userPassword(null);
    }

    // Flag indicating whether or not the submit verify password button is enabled
    self.verifyPasswordButtonEnabled = ko.computed(function () {
        return self.userPassword() && self.userPassword() !== "";
    })

    // Function to process the user's password verification
    self.submitVerifyPasswordDialog = function () {
        $.ajax({
            url: reauthenticateUrl,
            type: "POST",
            contentType: jsonContentType,
            data: JSON.stringify(self.userPassword()),
            dataType: "json",
            beforeSend: function (data) {
                self.verifyPasswordDialogIsDisplayed(false);
                self.verifyingPassword(true);
            },
            success: function (data) {
                if (!account.handleInvalidSessionResponse(data)) {
                    self.verifyingPassword(false);
                    self.verifyingPasswordSuccessFunction();
                    self.verifyPasswordMessage(null);
                    self.userPassword(null);
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                if (jqXHR.status != 0) {
                    self.verifyPasswordMessage(JSON.parse(jqXHR.responseText));
                }
                self.verifyingPassword(false);
                self.verifyPasswordDialogIsDisplayed(true);
            },
            complete: function () { }
        });
    }

    // Function called when password verification is successful
    self.verifyingPasswordSuccessFunction = function () { };

    self.saveProxySelectionButton = {
        id: "save-proxy-selection-dialog-button",
        isPrimary: true,
        title: Ellucian.Proxy.ButtonLabels.saveProxySelectionDialogButtonLabel,
        callback: self.saveProxySelectionDialog,
        enabled: self.saveProxySelectionEnabled
    };

    self.cancelProxySelectionButton = {
        id: "cancel-proxy-selection-dialog-button",
        isPrimary: false,
        title: Ellucian.Proxy.ButtonLabels.cancelProxySelectionDialogButtonLabel,
        callback: self.cancelProxySelectionDialog
    };

    self.verifyPasswordDialogButton = {
        id: "verify-password-dialog-button",
        isPrimary: true,
        title: Ellucian.SharedComponents.ButtonLabels.buttonTextVerifyPassword,
        callback: self.submitVerifyPasswordDialog,
        enabled: self.verifyPasswordButtonEnabled
    };

    self.cancelPasswordDialogButton = {
        id: "cancel-dialog-button",
        title: Ellucian.SharedComponents.ButtonLabels.buttonTextCancel,
        callback: self.cancelVerifyPasswordDialog
    };

    $(document).ready(function () {

        // On page resize, check if we need to adapt the page for mobile.
        // thottleResize is used with a timeout to prevent repeated calls to checkForMobile in a single resize
        var throttleResize = 0;
        $(window).on("resize", function () {
            clearTimeout(throttleResize);
            throttleResize = setTimeout(function () { self.checkForMobile(window, document) }, 100);
        });
    });
};

/////////////////////////////////////////////////////////////////////////////////////////////
//  This file contains utility functionality that needs to be made available across
//  two or more views within the Student area of Self Service. Functions placed into this
//  file should be as succint and context-less as possible. These functions will be available
//  in every view in the Student area, and loaded in the Student _ViewStart page. Do NOT place
//  anything into this file that needs to be available in other Areas, such as Finance.
/////////////////////////////////////////////////////////////////////////////////////////////

// Determine the value to display for a given Section's credits amount
function courseOrSectionCreditsCeusDisplay(item) {
    var display = "";
    if (item != null) {
        display = ((item.MaximumCredits != null) && item.MaximumCredits > 0 && item.MaximumCredits !== item.MinimumCredits) ? item.MinimumCredits + " to " + item.MaximumCredits : item.MinimumCredits;
        if (display === null) {
            if (item.Ceus != null) {
                display = item.Ceus + " CEUs";
            }
        }
    }
    return display;
}

// Determine if the given course or section item represents a variable credit item
function isCourseOrSectionVariableCredit(item) {
    return (item != null && item.MinimumCredits != null && item.MaximumCredits != null &&
        item.MinimumCredits != item.MaximumCredits);
}

// Time of day validation common logic
function checkValidTimeFormat(timeExact) {

    var amTimeSuffix = "am";
    var pmTimeSuffix = "pm";
    var timeValue = timeExact.toLowerCase().replace(amTimeSuffix, '').replace(/ /g, '')
        .replace(pmTimeSuffix, '').replace(':', '').replace('.', '');

    //check if timeValue is not a number
    if (isNaN(timeValue)) { return null; }

    var timeExactValue = null;
    //check valid values with length of number supplied
    switch (timeValue.length) {
        case 1:
        case 2:
            timeExactValue = moment(timeExact, "hA").format("hh:mm A");
            break;
        case 3:
            var newTimeExact = timeValue[0] + '.' + timeValue[1] + timeValue[2] + (timeExact.toLowerCase().indexOf(pmTimeSuffix) > 0 ? pmTimeSuffix : amTimeSuffix);
            timeExactValue = moment(newTimeExact, "h.mmA").format("hh:mm A");
            break;
        case 4:
            timeExactValue = moment(timeExact, "hhmmA").format("hh:mm A");
            break;
    }
    if (timeExactValue == "Invalid date") {
        return null;
    }
    return timeExactValue;
}
// Copyright 2012 -2023 Ellucian Company L.P. and its affiliates.

function catalogSearchSubjectsViewModel() {
    var self = this;
    
    this.subjects = ko.observableArray();
    this.filter = ko.observable("");


    this.filteredSubjects = ko.computed(function () {
       
            return ko.utils.arrayFilter(self.subjects(), function (subject) {
                return subject.Description().toUpperCase().indexOf(self.filter().toUpperCase()) !== -1;

            });
       
    });


    this.isLoaded = ko.observable(false);

    // catalogResultsDisplayed is used by embedded (asynchronous) course catalogs.  It subscribes to the
    // "catalogResultDisplayed" topic.  When a search result is displayed, the process displaying the result should
    // post a message (with value of true) to the "catalogResultDisplayed" topic, allowing the catalog index to hide.
    this.catalogResultsDisplayed = ko.observable(false).subscribeTo("catalogResultDisplayed");

};

// searchUrl = string - the url to which to submit search requests
// searchAsync = boolean - submit search request async via Ajax, or sync as a page request
function subjectModel(data, baseUrl, searchAsync) {
    ko.mapping.fromJS(data, {}, this);

    var self = this;

    this.searchUrl = ko.observable(baseUrl + "?subjects=" + encodeURIComponent(self.Code()));

    this.searchAsync = ko.observable(searchAsync).publishOn("searchAsync");

    // When a search result is requested and received, publish to the "catalogResult" topic,
    // allowing the result's view model to display the data.
    this.catalogResult = ko.observable(null).publishOn("catalogResult", true);

    // catalogResultsDisplayed is used by embedded (asynchronous) course catalogs.  It posts to the
    // "catalogResultDisplayed" topic.  When a search result is displayed, post a message (with value of true) 
    // to the "catalogResultDisplayed" topic, allowing the catalog index to hide.
    this.catalogResultsDisplayed = ko.observable(false).publishOn("catalogResultDisplayed", true);

    this.clickHandler = function () {

        if (self.searchAsync() == false) {
            return true;
        } else {
            var query = "";
            if (window.location.href.indexOf('?') !== -1) {
                query = window.location.href.slice(window.location.href.indexOf('?'));
            }
            var searchUrl = self.searchUrl() + query;
            $.ajax({
                url: searchUrl,
                type: "GET",
                contentType: jsonContentType,
                dataType: "json",
                success: function (data) {
                    if (!account.handleInvalidSessionResponse(data)) {
                        self.catalogResult(data);
                        if (self.catalogResultsDisplayed() !== true) {
                            self.catalogResultsDisplayed(true);
                        } else {
                            self.catalogResultsDisplayed.valueHasMutated();
                        }
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    if (jqXHR.status != 0)
                        $('#notificationHost').notificationCenter('addNotification', { message: "Unable to retrieve search results.", type: "error" });
                },
                complete: function (jqXHR, textStatus) {

                }
            });
            return false;
        }
    };
}

// Copyright 2012 - 2017 Ellucian Company L.P. and its affiliates

Ellucian.CatalogAdvancedSearch = Ellucian.CatalogAdvancedSearch || {};
Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchKeywordComponentsModel = function () {
    var self = this;
    this.synonym = ko.observable("");
    this.synonym.ErrorMessage = ko.observable("");
    this.synonym.isValid = ko.computed(function () {
        self.synonym.ErrorMessage("");
        var parsedValue = Globalize.parseInt(self.synonym());
        if (isNaN(parsedValue) && !isNullOrEmpty(self.synonym())) {
            self.synonym.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.invalidSynonymFormatErrorMessage);
            return false;
        }
        return true;
    });
    this.subject = ko.observable("");
    this.subject.isEnabled = ko.computed(function () {
        if (isNullOrEmpty(self.synonym())) {
            return true;
        }
        else {
            self.subject("");
            return false;
        }
    });
    this.courseNumber = ko.observable("");
    this.courseNumber.isEnabled = ko.computed(function () {
        if (isNullOrEmpty(self.subject())) {
            self.courseNumber("");
            return false;
        }
        else {
            return true;
        }
    });
    this.section = ko.observable("");
    this.section.isEnabled = ko.computed(function () {
        if (isNullOrEmpty(self.courseNumber())) {
            self.section("");
            return false;
        }
        else {
            return true;
        }
    });
    this.isNullOrEmpty = function () {
        if (isNullOrEmpty(self.subject()) && isNullOrEmpty(self.courseNumber()) && isNullOrEmpty(self.section()) && isNullOrEmpty(self.synonym())) {
            return true;
        }
        else
            return false;
    };
    //verify validity for keyword component
    this.isValid = ko.computed(function () {
        if (self.synonym.isValid() === false) {
            return false;
        }
        else {
            return true;
        }
    });
};


// Copyright 2012-2023 Ellucian Company L.P. and its affiliates.
Ellucian.CatalogAdvancedSearch = Ellucian.CatalogAdvancedSearch || {};
//This model is being used by catalog advanced search for guest users, student planning users, non-student planning users.
Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchViewModel = function (postAdvancedSearchUrl, isAdvisor) {
    var self = this;
    
    BaseViewModel.call(self, 768);

    // Check for mobile and set the isMobile observable as determined (defined in BaseViewModel.
    self.checkForMobile(window, document);

    this.Subjects = ko.observableArray();
    this.TopicCodes = ko.observableArray();
    this.Terms = ko.observableArray();
    this.Locations = ko.observableArray();
    this.AcademicLevels = ko.observableArray();
    this.CourseTypes = ko.observableArray();
    this.TimeRanges = ko.observableArray();
    this.DaysOfWeek = ko.observableArray();
    this.LatestSearchDate = ko.observable(null);
    this.EarliestSearchDate = ko.observable(null);
    this.DefaultSearchResultView = ko.observable(Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchResultsViewTypes.course);

    this.catalogAdvancedSearchDataModelInstance = new Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchDataModel(self);
    this.isLoaded = ko.observable(false);
    this.isSubmissionInProgress = ko.observable(false);

    // When a search result is requested and received, publish to the "catalogResult" topic,
    // allowing the result's view model to display the data.
    this.catalogResult = ko.observable(null).publishOn("catalogResult", true);
    // catalogResultsDisplayed is used by embedded (asynchronous) course catalogs.  It posts to the
    // "catalogResultDisplayed" topic.  When a search result is displayed, post a message (with value of true) 
    // to the "catalogResultDisplayed" topic, allowing the catalog index to hide.
    this.catalogResultsDisplayed = ko.observable(false).publishOn("catalogResultDisplayed", true);

    this.dirtyFlag = new ko.dirtyFlag(self.catalogAdvancedSearchDataModelInstance, false);

    //all labels and messages. This is mostly used by regular advanced search catalog. For IE catalog labels are provided directly in markup
    this.labels={
        academicLevelLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.academicLevelLabel),
        addCourseButtonLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.addCourseButtonLabel),
        clearButtonLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.clearButtonLabel),
        dayOfWeekLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.dayOfWeekLabel),
        endMeetingDateLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.endMeetingDateLabel),
        headerText:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.headerText),
        locationLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.locationLabel),
        searchButtonLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.searchButtonLabel),
        startMeetingDateLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.startMeetingDateLabel),
        subjectSelectCaption:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.subjectSelectCaption),
        termLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.termLabel),
        termSelectCaption:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.termSelectCaption),
        timeOfDayLabel:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.timeOfDayLabel),
        timeOfDayCaption:ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.timeOfDayCaption),
        courseLabel: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.courseLabel),
        synonymLabel: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.synonymLabel),
        locationSelectCaption : ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.locationSelectCaption),
        academicLevelSelectCaption: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.academicLevelSelectCaption),
        courseTypeLabel:  ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.courseTypeLabel),
        courseTypeSelectCaption: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.courseTypeSelectCaption),
        timeOfDayPlaceholderText: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.timeOfDayPlaceholderText),
        timeOfDayFromLabelAdvanceSearch: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.timeOfDayFromLabelAdvanceSearch),
        timeOfDayToLabelAdvanceSearch: ko.observable(Ellucian.CatalogAdvancedSearch.MarkupLabels.timeOfDayToLabelAdvanceSearch)
    };


    this.areDaysSelected = ko.computed(function () {
        
            //move selected days to course search data model
            for (var i = 0; i < self.DaysOfWeek().length; i++) {
                if (self.DaysOfWeek()[i].Item3() == true) {
                    return true;
                }
            }
            return false;
    });

    this.isSearchEnabled = ko.computed(function () {
        return self.dirtyFlag.isDirty() || self.areDaysSelected();
    });    

    this.submit = function () {
        try {
            self.isSubmissionInProgress(true);
            //move selected days to course search data model
            for (var i = 0; i < self.DaysOfWeek().length; i++) {
                if(self.DaysOfWeek()[i].Item3()==true)
                {
                    self.catalogAdvancedSearchDataModelInstance.days.push(self.DaysOfWeek()[i].Item2());
                }
            }
            //when its not an advisor then advanced search criteria will make Http POST request to load search result page
            if (!isAdvisor) {
                ko.utils.postJson(postAdvancedSearchUrl, {
                    searchParams: self.catalogAdvancedSearchDataModelInstance.mapToJS(),
                    __IsGuestUser: isGuest,
                    __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
                });
            } else {
                //when it is advisor then we have to make ajax request to retrieve search result and update the result in topic catalogResult so that when this topic is updated  
                //a proper catalog result view is displayed. Since advisees page follows SPA model, everything is loaded upfront and more is left on updating the markup through appropriate hide and show.
                catalogSearchCriteriaModelInstance = new catalogSearchCriteriaModel();
                ko.mapping.fromJS(self.catalogAdvancedSearchDataModelInstance.mapToJS(), {}, catalogSearchCriteriaModelInstance);//mapping will include catalogSearchView from advanced search to search critieria instance as observable
                catalogSearchCriteriaModelInstance.sortOn = "2";
                catalogSearchCriteriaModelInstance.sortDirection = "0";
                var advanceSearchUrl = Ellucian.Course.ActionUrls.postAdvancedSearchAjaxUrl;
                $.ajax({
                    url: advanceSearchUrl,
                    type: "POST",
                    data: ko.toJSON(self.catalogAdvancedSearchDataModelInstance.mapToJS()),
                    contentType: jsonContentType,
                    dataType: "json",
                    beforeSend: function () {
                    },
                    success: function (data) {
                        if (!account.handleInvalidSessionResponse(data)) {
                            self.catalogResult(data);                            
                            if (self.catalogResultsDisplayed() !== true) {
                                self.catalogResultsDisplayed(true);
                            } else {
                                self.catalogResultsDisplayed.valueHasMutated();
                            }

                            if (catalogResultViewModelInstance != null) {
                                var start = catalogResultViewModelInstance.StartTime();
                                var end = catalogResultViewModelInstance.EndTime();
                                for (var i = 0; i < catalogResultViewModelInstance.TimeRanges.length; i++) {
                                    if (start == catalogResultViewModelInstance.TimeRanges[i].StartTime && end == catalogResultViewModelInstance.TimeRanges[i].EndTime) {
                                        catalogResultViewModelInstance.SelectedTime(catalogResultViewModelInstance.TimeRanges[i]);
                                        catalogResultViewModelInstance.TimeFilterButtonValue(catalogResultViewModelInstance.TimeRanges[i]);
                                    }
                                }
                            }

                            $("#section-results-table").makeTableResponsive();

                        }
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        if (jqXHR.status != 0)
                            $('#notificationHost').notificationCenter('addNotification', { message: Ellucian.Courses.Resources.UnableToRetrieveSearchOptionMessage, type: "error" });
                    },
                    complete: function (jqXHR, textStatus) {
                        self.isSubmissionInProgress(false);
                        self.clear();
                    }
                });
            }
        }
        catch (e) {
            self.isSubmissionInProgress(false);
            console.error(e.message);
            $('#notificationHost').notificationCenter('addNotification', { message: 'advanced search failed', type: "error"
            });
        }
    };

    this.clear = function () {
        for (var i = 0; i < self.DaysOfWeek().length; i++) {
            self.DaysOfWeek()[i].Item3(false);
    }
      return self.catalogAdvancedSearchDataModelInstance.clear();
    };
};

Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchResultsViewTypes = {
    course:"CatalogListing",
    section: "SectionListing"
}

Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchDataModel=function(parent) {
    var self = this;
    this.term = ko.observable();
    this.location = ko.observable();
    this.academicLevel = ko.observable();
    this.courseType = ko.observable();
    this.startDate = ko.observable("");
    this.endDate = ko.observable("");
    this.startsAtTime = ko.observable();
    this.endsByTime = ko.observable();
    this.synonym = ko.observable();
    //this idenitifes if course listing or section listing is selected. later on this is mapped to catalog search criteria model used in search result page
    this.searchResultsView = ko.observable(Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchResultsViewTypes.course);

    this.startsAtTime.ErrorMessage = ko.observable("");
    this.startsAtTime.isValid = ko.computed(function () {
        self.startsAtTime.ErrorMessage("");
        if (!isNullOrEmpty(self.startsAtTime()) && self.startsAtTime() != "undefined") {
            var parseTimeValue = checkValidTimeFormat(self.startsAtTime());
            if (parseTimeValue === null) {
                self.startsAtTime.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.invalidTimeFormatErrorMessage);
                return false;
            }
            else {
                self.startsAtTime(parseTimeValue);
                return true;
            }
        }
        return true;
    });
   
    this.endsByTime.ErrorMessage = ko.observable("");
    this.endsByTime.isValid = ko.computed(function () {
        self.endsByTime.ErrorMessage("");
        if (!isNullOrEmpty(self.endsByTime()) && self.endsByTime() != "undefined") {
            var parseTimeValue = checkValidTimeFormat(self.endsByTime());
            if (parseTimeValue === null) {
                self.endsByTime.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.invalidTimeFormatErrorMessage);
                return false;
            }
            else {
                self.endsByTime(parseTimeValue);
                return true;
            }
        }
        return true;
    });

    this.startDate.ErrorMessage = ko.observable("");
    this.startDate.isValid = ko.computed(function () {

        if (!isNullOrEmpty(self.startDate()) && Globalize.parseDate(self.startDate()) == null) {
            self.startDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.invalidDateFormatErrorMessage);
            return false;
        }

        else if (!isNullOrEmpty(self.startDate()) && !isNullOrEmpty(self.endDate())) {
            if (moment(self.startDate()) > moment(self.endDate())) {
                self.startDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.startMeetingDateImproperMessage);
                return false;
            }
            else if (moment(self.startDate()) < moment(parent.EarliestSearchDate()) || moment(self.startDate()) > moment(parent.LatestSearchDate())) {
                self.startDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.startMeetingDateRangeMessage + " " + parent.EarliestSearchDate().toLocaleDateString() + " " + Ellucian.Course.SearchResult.timesDatesDelimiter + " " + parent.LatestSearchDate().toLocaleDateString());
                return false;
            }
            else {
                return true;
            }
        }
        else {
            return true;
        }
    });

    this.endDate.ErrorMessage = ko.observable("");
    this.endDate.isValid = ko.computed(function () {
        if (!isNullOrEmpty(self.endDate()) && Globalize.parseDate(self.endDate()) == null) {
            self.endDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.invalidDateFormatErrorMessage);
            return false;
        }

        else if (!isNullOrEmpty(self.startDate()) && !isNullOrEmpty(self.endDate())) {

            if (moment(self.endDate()) < moment(self.startDate())) {
                self.endDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.endMeetingDateImproperMessage);
                return false;
            }
            else if (moment(self.endDate()) > moment(parent.LatestSearchDate()) || moment(self.endDate()) < moment(parent.EarliestSearchDate())) {
                self.endDate.ErrorMessage(Ellucian.CatalogAdvancedSearch.MarkupLabels.endMeetingDateRangeMessage + " " + parent.EarliestSearchDate().toLocaleDateString() + " " + Ellucian.Course.SearchResult.timesDatesDelimiter + " " + parent.LatestSearchDate().toLocaleDateString());
                return false;
            }
            else {
                return true;
            }
        }
        else {
            return true;
        }
    });


    this.days = ko.observableArray();
    this.timeRange = ko.observable();
    this.keywordComponents = ko.observableArray();
    if (self.keywordComponents !== null && self.keywordComponents() !== null && self.keywordComponents().length == 0) {
        for (var i = 0; i < (parent.isMobile() ? 1 : 3); i++) {
            this.keywordComponents.push(new Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchKeywordComponentsModel());
        }
    }
    //validate if kewyword components is valid
    this.keywordComponents.isValid = ko.pureComputed(function () {
        if (self.keywordComponents !== null && self.keywordComponents().length > 0) {
            for (var i = 0; i < self.keywordComponents().length; i++) {
                if (self.keywordComponents()[i].isValid() == false) {
                    return false;
                }
            }
            return true;
        }
        else {
            return true;
        }
    });

    this.addCourse = function () {
        this.keywordComponents.push(new Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchKeywordComponentsModel());
    };

    this.isValid = ko.pureComputed(function () {
        if (self.endDate.isValid() && self.startDate.isValid() && self.startsAtTime.isValid() && self.endsByTime.isValid() && self.keywordComponents.isValid()) {
            return true;
        }
        else {
            return false;
        }
    });

    this.clear = function () {
        var tempKeywords = [];
        self.term("");
        self.location("");
        self.startDate("");
        self.endDate("");
        self.startsAtTime(null);
        self.endsByTime(null);
        self.days([]);
        self.academicLevel("");
        self.courseType("");
        self.synonym("");
        self.searchResultsView(parent.DefaultSearchResultView());
        self.timeRange(null);
        self.keywordComponents([]);
        for (var i = 0; i < (parent.isMobile() ? 1 : 3); i++) {
            this.keywordComponents.push(new Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchKeywordComponentsModel());
        }
        return;
    };

    this.mapToJS = function () {
        var self = this;
        var tempKeywords = [];
        if (self.keywordComponents !== null && self.keywordComponents().length > 0) {
            for (var i = 0; i < self.keywordComponents().length; i++) {
                if (self.keywordComponents()[i].isNullOrEmpty() == false) {
                    tempKeywords.push(self.keywordComponents()[i]);
                }
            }
            self.keywordComponents([]);
            self.keywordComponents.pushAll(tempKeywords);
        }
        var copy = ko.mapping.toJS(self);
        return copy; //return the copy to be serialized
    };

};



// Copyright 2012 -2023 Ellucian Company L.P. and its affiliates.

function catalogSearchViewModel() {
    var self = this;
    self.catalogAdvancedSearchViewModelInstance = new Ellucian.CatalogAdvancedSearch.catalogAdvancedSearchViewModel(Ellucian.Course.ActionUrls.postSearchResultUrl, false);
    self.catalogSearchSubjectViewModelInstance = new catalogSearchSubjectsViewModel();
    self.TabSelected = ko.observable(1);
    self.setTabSelected = function (defaultSearchView) {
        self.TabSelected(1);
        if (typeof defaultSearchView !== 'undefined' && defaultSearchView === "AdvancedSearch") {
            self.TabSelected(2);
        }
    }
    self.initialization = function () {
         $.ajax({
        url: Ellucian.Course.ActionUrls.getAdvancedSearchUrl,
        type: "GET",
        contentType: jsonContentType,
        dataType: "json",
        success: function (data) {
            if (!account.handleInvalidSessionResponse(data)) {
                // Set the default search view
                self.setTabSelected(data.DefaultSearchView);
                ko.mapping.fromJS(data, {}, self.catalogAdvancedSearchViewModelInstance);
                self.catalogAdvancedSearchViewModelInstance.catalogAdvancedSearchDataModelInstance.searchResultsView(data.DefaultSearchResultView);
                var subjects = [];
                for (var i = 0; i < data.Subjects.length; i++) {
                    subjects.push(new subjectModel(data.Subjects[i], Ellucian.Course.ActionUrls.searchCourseCatalogActionUrl, false));

                }
                self.catalogSearchSubjectViewModelInstance.subjects.pushAll(subjects);
                
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            if (jqXHR.status != 0)
                $('#notificationHost').notificationCenter('addNotification', { message: "Unable to retrieve search options.", type: "error" });
        },
        complete: function (jqXHR, textStatus) {
            self.catalogAdvancedSearchViewModelInstance.isLoaded(true);
            self.catalogSearchSubjectViewModelInstance.isLoaded(true);
        }
    });
};
};
var catalogSearchViewInstance = new catalogSearchViewModel();


$(document).ready(function () {
    ko.applyBindings(catalogSearchViewInstance, document.getElementById("main-content"));
    catalogSearchViewInstance.initialization();
});

