component-search-service.js

/**
 * Model for search services
 * @module SearchServiceModel
 * @param  {jQuery} $ Instance of jQuery
 * @param  {Document} document dom document object
 * @return {SearchServiceModel} instance of search service model
*/
XA.component.search.service = (function ($, document) {

    'use strict';

    var searchResultsModels = XA.component.search.results.searchResultModels,
        urlHelperModel = XA.component.search.url,
        queryModel = XA.component.search.query,
        queryParameters = XA.component.search.parameters,
        apiModel = XA.component.search.ajax,
        SearchServiceModel;
    /**
     * @name module:SearchServiceModel.SearchServiceModel
     * @constructor
     * @augments Backbone.Model
     */
    SearchServiceModel = Backbone.Model.extend(
        /** @lends module:SearchServiceModel.SearchServiceModel.prototype **/
        {
            /**
            * Default model options
            * @default
            */
            defaults: {
            },
            /**
            * Watches for event 'orderChanged' and trigger
            * ['getData']{@link module:SearchServiceModel.SearchServiceModel#getData}
            * @listens module:XA.component.search.vent~event:orderChanged
            * @memberof module:SearchServiceModel.SearchServiceModel
            */
            initialize: function () {
                var that = this;
                XA.component.search.vent.on('orderChanged', function (data) {
                    that.getData(data);
                });
            },
            /**
            * If search result model created call
            * ['getSearchResultsData']{@link module:SearchServiceModel.SearchServiceModel#getSearchResultsData}
            * in other case call 
            * ['getEndpointAndSearch']{@link module:SearchServiceModel.SearchServiceModel#getEndpointAndSearch} 
            * @param {Object} overrideProps new properties fro overriding
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#getData
            */
            getData: function (overrideProps) {
                if (searchResultsModels.length > 0) {
                    this.getSearchResultsData(overrideProps);
                } else {
                    this.getEndpointAndSearch(overrideProps);
                }
            },
            /**
            * Processes search result data
            * @param {Object} overrideProps properties for overriding
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#getSearchResultsData
            */
            getSearchResultsData: function (overrideProps) {
                var hash = queryModel.parseHashParameters(window.location.hash),
                    that = this,
                    defaultSortOrder,
                    offsetSignature,
                    searchResultsDefaultPageSize,
                    searchResultsDefaultVariant,
                    signature,
                    mergedProps,
                    url;

                _.each(searchResultsModels, function (searchModel) {
                    signature = searchModel.get('dataProperties').sig !== null
                        ? encodeURIComponent(searchModel.get('dataProperties').sig)
                        : "";
                    offsetSignature = signature !== '' ? signature + "_e" : "e";

                    if (!searchModel.get('dataProperties').autoFireSearch) {
                        //If hash is not present or no criteria with search model signature is present in hash, then do not fire search
                        if (!hash || !queryModel.isSignaturePresentInHash(hash, signature)) {
                            return;
                        }
                    }

                    defaultSortOrder = searchModel.get('dataProperties').defaultSortOrder;
                    searchResultsDefaultPageSize = searchModel.get('dataProperties').p;
                    searchResultsDefaultVariant = searchModel.get('dataProperties').v;

                    //if we have singleRequestMode with signature, then we are getting data just for that signature
                    if (typeof overrideProps !== "undefined" &&
                        overrideProps.hasOwnProperty("singleRequestMode") &&
                        overrideProps["singleRequestMode"] != signature) {
                        return;
                    }

                    //if there is no page size param in the query, but we have page size component on the page, then take
                    //default page size or first (if default isn't set)
                    hash = that.getDefaultDefaultPageSize(signature, searchResultsDefaultPageSize, hash);

                    //in case of facet that filters items by language
                    //when someone is filtering by certain language, and removes that filter, then without
                    //this if statement, search results language parameter would not be considered - because l="" from hash object
                    //will override default language from dataProperties
                    if (hash.hasOwnProperty('l') && hash.l === '') {
                        hash.l = searchModel.get('dataProperties').l;
                    }

                    mergedProps = $.extend({}, searchModel.get('dataProperties'), hash);
                    mergedProps = $.extend(mergedProps, overrideProps);

                    if (mergedProps.hasOwnProperty('loadMore')) {
                        searchModel.set('loadMore', true);
                        delete mergedProps.loadMore;

                        var newOffset = searchModel.get('loadMoreOffset') + mergedProps.p;
                        searchModel.set('loadMoreOffset', newOffset);
                        mergedProps[offsetSignature] = newOffset;
                    }
                    else if (searchModel.get('loadMoreOffset')) { //if results reload is caused by variant change, loadMoreOffset should be considered in request, so after changing variant, the same number of results will be visible
                        if (mergedProps.p !== 0) {
                            mergedProps.p = mergedProps.p * (1 + searchModel.get('loadMoreOffset') / mergedProps.p);
                            mergedProps[offsetSignature] = 0;
                            searchModel.set('loadMoreOffset', 0);
                        }
                        delete mergedProps.variantChanged;
                    }

                    mergedProps = that.getSortOrder(signature, mergedProps, defaultSortOrder);
                    mergedProps = that.setVariant(signature, mergedProps, searchResultsDefaultVariant);

                    url = urlHelperModel.createSearchUrl(mergedProps, signature);
                    if (!url) {
                        return;
                    }

                    if (!searchModel.checkBlockingRequest()) {
                        searchModel.blockRequests(true);
                        XA.component.search.vent.trigger('results-loading', searchModel.cid);
                        apiModel.getData({
                            callback: function (data) {
                                data.Signature = data.Signature !== null ? data.Signature : '';
                                XA.component.search.vent.trigger('results-loaded', {
                                    dataCount: data.Count,
                                    data: data.Results,
                                    pageSize: data.Signature !== '' && mergedProps.hasOwnProperty(data.Signature + '_p') ? mergedProps[data.Signature + '_p'] : mergedProps.p,
                                    offset: data.Signature !== '' && mergedProps.hasOwnProperty(data.Signature + '_e') ? mergedProps[data.Signature + '_e'] : mergedProps.e,
                                    searchResultsSignature: data.Signature,
                                    loadMore: searchModel.get('loadMore')
                                });
                            },
                            url: url
                        });
                    }
                });
            },
            /**
            * Processes search result data. Call for apiModel.getData
            * @param {Object} overrideProps properties for overriding
            * @fires XA.component.search.vent#results-loaded
            * @listens module:XA.component.search.vent~event:orderChanged
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#getSearchData
            */
            getSearchData: function (overrideProps) {
                var hash = queryModel.parseHashParameters(window.location.hash),
                    mergedProps = $.extend({}, hash, mergedProps, overrideProps),
                    signatures = [],
                    firstSortingOption,
                    url,
                    i;


                mergedProps = this.getDefaultDefaultPageSize("", 0, mergedProps);


                //if there is no sort param in the query, but we have sort results component on the page, then take first sorting option
                if (!mergedProps.hasOwnProperty('o') && typeof XA.component.search.sort !== 'undefined') {
                    firstSortingOption = XA.component.search.sort.getFirstSortingOption();
                    if (firstSortingOption !== -1) {
                        mergedProps.o = firstSortingOption;
                    }
                }

                //currently we are taking endpoint from Map component (atm it's only which can handle showing of search results)
                mergedProps.endpoint = XA.component.map.getSearchEndpoint();
                //the same with signature
                signatures = XA.component.map.getSignatures();

                for (i = 0; i < signatures.length; i++) {
                    mergedProps.sig = signatures[i];
                    url = urlHelperModel.createSearchUrl(mergedProps, signatures[i]);

                    if (!url) {
                        return;
                    }

                    apiModel.getData({
                        callback: function (data) {
                            data.Signature = data.Signature !== null ? data.Signature : '';
                            XA.component.search.vent.trigger('results-loaded', {
                                dataCount: data.Count,
                                data: data.Results,
                                pageSize: mergedProps.p,
                                offset: 0,
                                searchResultsSignature: data.Signature
                            });
                        },
                        url: url
                    });
                }
            },
            /**
           * Checks if map component exists and getSearchEndpoint returns not undefined value. If yes
           * call ["getSearchData"]{@link module:SearchServiceModel.SearchServiceModel#getSearchData}
           * @memberof module:SearchServiceModel.SearchServiceModel
           * @alias module:SearchServiceModel.SearchServiceModel#getEndpointAndSearch
           */
            getEndpointAndSearch: function () {
                if (XA.component.map && typeof (XA.component.map.getSearchEndpoint()) !== 'undefined') {
                    this.getSearchData();
                } else {
                    setTimeout(this.getEndpointAndSearch.bind(this), 100);
                }
            },
            /**
            * Sorts search result due to selected properties
            * @param {String} signature component signature
            * @param {Object} mergedProps 
            * @param {String} searchResultsDefaultSortOrder default sorting order
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#getSortOrder
            * @returns {Object} merged properties
            */
            getSortOrder: function (signature, mergedProps, searchResultsDefaultSortOrder) {
                var firstSortingOption = XA.component.search.sort.getFirstSortingOption(signature),
                    paramName = signature !== '' ? signature + '_o' : 'o',
                    obj = {},
                    defaultSortOrder = "",
                    defaultSigned = {};


                if (firstSortingOption !== -1) {
                    defaultSortOrder = firstSortingOption;
                } else if (searchResultsDefaultSortOrder !== "") {
                    defaultSortOrder = searchResultsDefaultSortOrder;
                }

                if (!mergedProps.hasOwnProperty(paramName) && defaultSortOrder !== "") {
                    delete mergedProps.defaultSortOrder;
                    delete mergedProps.o;

                    mergedProps[paramName] = defaultSortOrder;
                    obj[paramName] = defaultSortOrder;
                }
                defaultSigned[paramName] = defaultSortOrder;
                queryParameters.registerDefault(defaultSigned);

                return mergedProps;
            },

            /**
            * Sets default page size into hash
            * @param {String} signature component signature
            * @param {Number} searchResultsDefaultPageSize  default page size
            * @param {Object} hash hash represented as an object
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#getDefaultDefaultPageSize
            * @returns {Object} hash object
            */
            getDefaultDefaultPageSize: function (signature, searchResultsDefaultPageSize, hash) {
                var defaultPageSizes = XA.component.search.pageSize.getDefaultPageSizes(),
                    paramName = signature !== "" ? signature + "_p" : "p",
                    pageSizes;

                if (!hash.hasOwnProperty(paramName) && typeof XA.component.search.pageSize !== 'undefined') {
                    if (defaultPageSizes !== -1) {
                        pageSizes = defaultPageSizes.filter(function (x) {
                            return x.signatures.indexOf(signature) !== -1;
                        });
                        if (pageSizes.length > 0) {
                            hash[paramName] = pageSizes[0].defaultPageSize;
                        }
                    }
                }

                if (!hash.hasOwnProperty(paramName) && searchResultsDefaultPageSize != 0) {
                    hash[paramName] = searchResultsDefaultPageSize;
                }

                var defaultParam = {};
                defaultParam[paramName] = searchResultsDefaultPageSize;
                queryParameters.registerDefault(defaultParam);
                return hash;
            },
            /**
            * Sets variant of view search result based on variant selector component
            * @param {String} signature component signature
            * @param {Number} searchResultsDefaultPageSize  default page size
            * @param {Object} hash hash represented as an object
            * @memberof module:SearchServiceModel.SearchServiceModel
            * @alias module:SearchServiceModel.SearchServiceModel#setVariant
            * @returns {Object} hash object
            */
            setVariant: function (signature, mergedProps, searchResultsDefaultVariant) {
                var variantMappings = XA.component.search.variantFilter.getVariantMappings(signature),
                    paramName = signature !== "" ? signature + "_v" : "v",
                    variantIndex;

                if (signature === "" && $.isEmptyObject(variantMappings)) {
                    //there is no signature, and no variant selector on the page, therefore, do nothing - mergedPropes should have v param
                } else if (signature === "" && mergedProps.hasOwnProperty(paramName) && variantMappings.hasOwnProperty(mergedProps[paramName])) {
                    //there is no signature, but there is variant selector on the page, and it was clicked
                    mergedProps[paramName] = variantMappings[mergedProps[paramName]].id;
                } else if (signature === "" && variantMappings.hasOwnProperty(0)) {
                    //there is no signature, but there is variant selector on the page, therefore take its first variant
                    mergedProps[paramName] = variantMappings[0].id;
                } else {
                    delete mergedProps.v;
                    if (mergedProps.hasOwnProperty(paramName)) {
                        //variant switcher was clicked
                        mergedProps[paramName] = variantMappings[mergedProps[paramName]].id;
                    } else {
                        //if there is variant selector on the page, take first variant form the component, otherwise use search results default variant
                        if (variantMappings.hasOwnProperty(0)) {
                            mergedProps[paramName] = variantMappings[0].id;
                        } else {
                            mergedProps[paramName] = searchResultsDefaultVariant;
                        }
                    }
                }

                var defaultVariant = {};
                defaultVariant[paramName] = 0;
                queryParameters.registerDefault(defaultVariant);

                return mergedProps;
            }
        });

    return new SearchServiceModel();

}(jQuery, document));