PageSourceSearch

https://www.revatio.com/-/media/base-themes/searchtheme/scripts/component-search-results-filter.js

js revatio.com collected 2026-10-04 22:02:42 UTC 25,527 bytes, 532 lines download raw bytes

1/**
2 * Facet facet result filter component functionality
3 * @module facetResultsFilter
4 * @param  {jQuery} $ Instance of jQuery
5 * @param  {Document} document dom document object
6 * @return {Object} list of methods for working with component facet result filter
7*/
8XA.component.search.facet.resultsfilter = (function ($, document) {
9    /**
10    * This object stores all public api methods
11    * @type {Object.<Methods>}
12    * @memberOf module:facetResultsFilter
13    */
14    var api = {},
15        urlHelperModel,
16        queryModel,
17        apiModel;
18    /**
19    * @name module:facetResultsFilter.FacetResultsFilterModel
20    * @constructor
21    * @augments Backbone.Model
22    */
23    var FacetResultsFilterModel = XA.component.search.baseModel.extend(
24        /** @lends module:facetResultsFilter.FacetResultsFilterModel.prototype **/
25        {
26            /**
27            * Default model options
28            * @default
29            */
30            defaults: {
31                template: "<div class='facet-search-filter <% if(!showAllFacets){%>facet-hided<%}%>'><% " +
32                    "_.forEach(facet.Values, function(value,key){" +
33                    "%><p class='facet-value <% if(highlightBehaviour<=key){ %> hide-facet-value <% } %>' data-facetValue='<%= value.Name !== '' ? encodeURI(value.Name) : '_empty_' %>'>" +
34                    "<span><%= value.Name !== '' ? value.Name : emptyText %> " +
35                    "<span class='facet-count'>(<%= value.Count %>)</span>" +
36                    "</span>" +
37                    "</p><%" +
38                    " }); %>" +
39                    "<% if(highlightBehaviour>=1 && resultsCount>highlightBehaviour){ %>" +
40                    "<div class='toogle-facet-visibility'><% if(showAllFacets){ %><%=showLessText%><%}else{%><%=showMoreText%><%} %></div>" +
41                    "<%}%>" +
42                    "</div>",
43
44                templateMulti: "<div class='facet-search-filter <% if(!showAllFacets){%>facet-hided<%}%>'><% " +
45                    "_.forEach(facet.Values, function(value,key){" +
46                    "%><p class='facet-value <% if(highlightBehaviour<=key){ %> hide-facet-value <% } %>' data-facetValue='<%= value.Name !== '' ? encodeURI(value.Name) : '_empty_' %>'>" +
47                    "<input type='checkbox' name='facetValue' />" +
48                    "<label for='facetName'><%= value.Name !== '' ? value.Name : emptyText %> " +
49                    "<span class='facet-count' data-facetCount='<%= value.Count %>'>(<%= value.Count %>)</span>" +
50                    "</label>" +
51                    "</p><%" +
52                    " }); %>" +
53                    "<% if(highlightBehaviour>=1 && resultsCount>highlightBehaviour){ %>" +
54                    "<div class='toogle-facet-visibility'><% if(showAllFacets){ %><%=showLessText%><%}else{%><%=showMoreText%><%} %></div>" +
55                    "<%}%>" +
56                    "</div>",
57                dataProperties: {},
58                blockNextRequest: false,
59                resultData: {},
60                timeStamp: '',
61                showAllFacets: false,
62                sig: []
63            },
64            /**
65            * Listens to changes on facets and hash
66           * @listens module:XA.component.search.vent~event:facet-data-loaded
67           * @listens module:XA.component.search.vent~event:facet-data-filtered
68           * @listens module:XA.component.search.vent~event:facet-data-partial-filtered
69           * @listens module:XA.component.search.vent~event:hashChanged
70           */
71            initialize: function () {
72                //event to get data at the begining or in case that there are no hash parameters in the url - one request for all controls
73                XA.component.search.vent.on("facet-data-loaded", this.processData.bind(this));
74                //if in the url hash we have this control facet name (someone clicked this control) then we have to listen for partial filtering
75                XA.component.search.vent.on("facet-data-partial-filtered", this.processData.bind(this));
76                //in case that we are not filtering by this control (not clicked)
77                XA.component.search.vent.on("facet-data-filtered", this.processData.bind(this));
78                //event after change of hash
79                XA.component.search.vent.on("hashChanged", this.updateComponent.bind(this));
80
81                this.set({ facetArray: [] });
82            },
83            /**
84             * Toggles value of blockNextRequest variable
85             * @memberof module:facetResultsFilter.FacetResultsFilterModel
86             * @alias module:facetResultsFilter.FacetResultsFilterModel#toggleBlockRequests
87             */
88            toggleBlockRequests: function () {
89                var state = this.get("blockNextRequest");
90                this.set(this.get("blockNextRequest"), !state);
91            },
92            /**
93            * Processes data that comes as parameter update
94            * model and sort facets
95            * @param {Object} data Data from server with facet values
96            * @memberof module:facetResultsFilter.FacetResultsFilterModel
97            * @alias module:facetResultsFilter.FacetResultsFilterModel#processData
98            */
99            processData: function (data) {
100                var inst = this,
101                    dataProperties = this.get('dataProperties'),
102                    sig = dataProperties.searchResultsSignature.split(','),
103                    sortOrder = dataProperties.sortOrder,
104                    i;
105
106                if (data.Signature === null) {
107                    data.Signature = "";
108                }
109
110
111                for (i = 0; i < sig.length; i++) {
112                    if (data.Facets.length > 0 && (data.Signature === sig[i])) {
113                        var facedData = _.find(data.Facets, function (f) {
114                            return f.Key.toLowerCase() === inst.get('dataProperties').f.toLowerCase();
115                        });
116                        if (facedData !== undefined) {
117                            this.sortFacetArray(sortOrder, facedData.Values);
118                            inst.set({ resultData: facedData });
119                        }
120                    }
121                }
122            },
123            /**
124             * Updates model value 'facetArray' with values from valuesString parameter
125             * @param {String} valuesString facet values separated by coma
126             * @memberof module:facetResultsFilter.FacetResultsFilterModel
127             * @alias module:facetResultsFilter.FacetResultsFilterModel#updateFacetArray
128             */
129            updateFacetArray: function (valuesString) {
130                if (valuesString) {
131                    var values = valuesString.split("||"),
132                        array = [];
133                    for (var i = 0; i < values.length; i++) {
134                        array.push(values[i]);
135                    }
136                    this.set({ facetArray: _.unique(array) });
137                }
138            },
139            /**
140             * Sets option selected value to model based on hash
141             * @param {Object} hash Hash stored as an object
142             * @memberof module:facetResultsFilter.FacetResultsFilterModel
143             * @alias module:facetResultsFilter.FacetResultsFilterModel#updateComponent
144             */
145            updateComponent: function (hash) {
146                var sig = this.get("sig");
147                for (i = 0; i < sig.length; i++) {
148                    if (!hash.hasOwnProperty(sig[i])) {
149                        this.set({ facetArray: [] });
150                    } else {
151                        this.updateFacetArray(hash[sig[i]]);
152                    }
153                    //in some cases change of facetArray doesn't trigger model change event (why?) and view isn't updates
154                    //and because of that timeStamp is updated which properly triggers model change event
155                    this.set("timeStamp", (new Date()).getTime());
156                }
157            }
158        });
159    /**
160    * @name module:facetResultsFilter.FacetResultsFilterView
161    * @constructor
162    * @augments Backbone.View
163    */
164    var FacetResultsFilterView = XA.component.search.baseView.extend(
165        /** @lends module:facetResultsFilter.FacetResultsFilterView.prototype **/
166        {
167            /**
168            * Initially sets data to model and watches events on which
169            * view should be updated
170            * @listens module:facetResultsFilter.FacetResultsFilterModel~event:change
171            * @memberof module:facetResultsFilter.FacetResultsFilterView
172            * @alias module:facetResultsFilter.FacetResultsFilterView#initialize
173            */
174            initialize: function () {
175                var dataProperties = this.$el.data(),
176                    hash = queryModel.parseHashParameters(window.location.hash),
177                    properties = dataProperties.properties,
178                    signatures,
179                    i;
180
181
182                if (dataProperties.properties.searchResultsSignature === null) {
183                    dataProperties.properties.searchResultsSignature = "";
184                }
185
186                signatures = this.translateSignatures(properties.searchResultsSignature, properties.f);
187
188                this.model.set({ dataProperties: properties });
189                this.model.set("sig", signatures);
190
191                for (i = 0; i < signatures.length; i++) {
192                    if (!jQuery.isEmptyObject(_.pick(hash, signatures[i]))) {
193                        var values = _.values(_.pick(hash, signatures[i]))[0];
194                        this.model.updateFacetArray(values)
195                    }
196                }
197
198                this.model.on("change", this.render, this);
199            },
200            /**
201             * list of events for Backbone View
202            * @memberof module:facetResultsFilter.FacetResultsFilterView
203            * @alias module:facetResultsFilter.FacetResultsFilterView#events
204             */
205            events: {
206                'click .facet-value': 'updateFacet',
207                'click .filterButton': 'updateFacet',
208                'click .clear-filter': 'removeFacet',
209                'click .bottom-remove-filter > button': 'removeFacet',
210                'click .toogle-facet-visibility': 'toogleFacetVisibility'
211            },
212
213            toogleFacetVisibility: function () {
214                var showFacets = this.model.get('showAllFacets');
215                this.model.set('showAllFacets', !showFacets);
216
217
218            },
219            /**
220            * Updates model 'facetArray' based on params
221            * @param {Event}  param Event object with current target
222            * @memberof module:facetResultsFilter.FacetResultsFilterView
223            * @alias module:facetResultsFilter.FacetResultsFilterView#updateFacet
224            */
225            updateFacet: function (param) {
226                var currentFacet = $(param.currentTarget),
227                    facetArray = this.model.get('facetArray'),
228                    properties = this.model.get('dataProperties'),
229                    facetClose = this.$el.find('.facet-heading > span'),
230                    facetGroup = currentFacet.parents('.component-content').find('.facet-search-filter'),
231                    facetName = properties.f.toLowerCase(),
232                    facetDataValue = currentFacet.data('facetvalue'),
233                    facetValue = typeof facetDataValue !== "undefined" ? decodeURIComponent(facetDataValue) : facetDataValue,
234                    sig = this.model.get('sig'),
235                    index,
236                    hash = {},
237                    i;
238
239                if (properties.multi) {
240                    if (facetValue) {
241                        if (currentFacet.is(':not(.active-facet)')) {
242                            this.setActiveFacet(facetName, facetValue);
243                            facetArray.push(facetValue);
244                        } else {
245                            currentFacet.removeClass('active-facet');
246
247                            currentFacet.find('[type=checkbox]').prop('checked', false);
248                            currentFacet.find('[type=checkbox] + label:after').css({ 'background': '#fff' });
249
250                            index = facetArray.indexOf(facetValue);
251                            if (index > -1) {
252                                facetArray.splice(index, 1);
253                            }
254
255                            if (facetArray.length == 0) {
256                                facetClose.removeClass('has-active-facet');
257                            }
258                        }
259                        this.model.set({ facetArray: facetArray });
260                    }
261
262                    //is there any better way to check what action start method?
263                    if (currentFacet[0].type == "button") {
264                        for (i = 0; i < sig.length; i++) {
265                            hash[sig[i]] = _.uniq(facetArray, function (item) {
266                                return JSON.stringify(item);
267                            }).join("||");
268                        }
269                        queryModel.updateHash(hash);
270                    }
271                } else {
272                    if (facetValue) {
273                        for (i = 0; i < sig.length; i++) {
274                            hash[sig[i]] = facetValue;
275                        }
276                        facetGroup.data('active-facet', hash);
277                        this.setActiveFacet(facetName, facetValue);
278                        queryModel.updateHash(hash);
279                    }
280                }
281
282            },
283            /**
284            * Sets default values for Search Result Filter and updates hash accordingly
285            * @param {Event} evt Event object
286            * @memberof module:facetResultsFilter.FacetResultsFilterView
287            * @alias module:facetResultsFilter.FacetResultsFilterView#removeFacet
288            */
289            removeFacet: function (evt) {
290                evt.preventDefault();
291
292                var facets = this.$el,
293                    facetClose = facets.find('.facet-heading > span'),
294                    facetValues = facets.find('.facet-value'),
295                    sig = this.model.get('sig');
296
297                queryModel.updateHash(this.updateSignaturesHash(sig, "", {}));
298
299                facetClose.removeClass('has-active-facet');
300
301                _.each(facetValues, function (single) {
302                    var $single = $(single);
303                    if ($single.hasClass('active-facet')) {
304                        $single.removeClass('active-facet');
305                        $single.find('[type=checkbox]').prop('checked', false);
306                        $single.find('[type=checkbox] + label:after').css({ 'background': '#fff' });
307                    }
308                });
309
310                this.model.set({ facetArray: [] });
311            },
312            /**
313            * Renders view
314            * @memberof module:facetResultsFilter.FacetResultsFilterView
315            * @alias module:facetResultsFilter.FacetResultsFilterView#render
316            */
317            render: function () {
318                var inst = this,
319                    resultData = this.model.get("resultData"),
320                    facetClose = this.$el.find('.facet-heading > span'),
321                    facetNames = this.model.get('dataProperties').f.split('|'),
322                    emptyValueText = this.model.get('dataProperties').emptyValueText,
323                    highlightThreshold = this.model.get('dataProperties').highlightThreshold,
324                    showLessText = this.model.get('dataProperties').showLessText,
325                    showMoreText = this.model.get('dataProperties').showMoreText,
326                    highlightBehaviour = parseInt(this.model.get('dataProperties').highlightBehaviour),
327                    showAllFacets = this.model.get('showAllFacets'),
328                    hash = queryModel.parseHashParameters(window.location.hash),
329                    sig = this.model.get('sig'),
330                    template, facetName, templateResult;
331
332                //checks if page is opened from disc - if yes then we are in Creative Exchange mode
333                if (window.location.href.startsWith("file://")) {
334                    return;
335                }
336                this.manageVisibilityByData(this.$el, resultData)
337                if (resultData !== undefined) {
338                    if (inst.model.get('dataProperties').multi === true) {
339                        template = _.template(inst.model.get("templateMulti"));
340                    } else {
341                        template = _.template(inst.model.get("template"));
342                    }
343                    templateResult = template({
344                        facet: resultData, emptyText: emptyValueText, showLessText: showLessText,
345                        showMoreText: showMoreText, highlightBehaviour: highlightBehaviour, showAllFacets: showAllFacets,
346                        resultsCount: resultData.Values ? resultData.Values.length : -1
347                    });
348                }
349
350                inst.$el.find(".contentContainer").html(templateResult);
351
352                //checks url hash for facets and runs setActiveFacet method for each facet filter
353                _.each(facetNames, function (val) {
354                    facetName = val.toLowerCase();
355                    for (var i = 0; i < sig.length; i++) {
356                        if (!jQuery.isEmptyObject(_.pick(hash, sig))) {
357                            var values = _.values(_.pick(hash, sig))[0];
358                            if (values) {
359                                inst.setActiveFacet(facetName, values);
360
361                                //If this rendering is supporting multiple signatures, we will mark active facet once.
362                                return;
363                            }
364                        }
365                    }
366                });
367
368                //highlights facets count greater than chosen threshold
369                if (highlightThreshold) {
370                    this.handleThreshold(highlightThreshold);
371                }
372
373                //if no facet is selected, remove previously highlighted cross icon (while back button)
374                if (this.model.get("facetArray").length === 0) {
375                    facetClose.removeClass('has-active-facet');
376                } else {
377                    facetClose.addClass('has-active-facet');
378                }
379            },
380            /**
381            * Manages search result filter active state
382            * @param {String} facetGroupName facet name
383            * @param {String} facetValueName facet value
384            * @memberof module:facetResultsFilter.FacetResultsFilterView
385            * @alias module:facetResultsFilter.FacetResultsFilterView#setActiveFacet
386            */
387            setActiveFacet: function (facetGroupName, facetValueName) {
388                var properties = this.model.get('dataProperties'),
389                    facetChildren = this.$el.find('p[data-facetvalue]'),
390                    facetClose = this.$el.find('.facet-heading > span'),
391                    inst = this,
392                    facetValue,
393                    values;
394
395                facetValueName = facetValueName.toString().toLowerCase();
396                facetValue = this.$el.find("[data-facetvalue]").filter(function () {
397                    return decodeURIComponent($(this).attr("data-facetvalue").toLowerCase()) === facetValueName;
398                });
399
400
401                if (typeof (facetValueName) !== "undefined" && facetValueName !== null) {
402                    values = facetValueName.split("||");
403                } else {
404                    return;
405                }
406
407                if (values.length > 1) {
408                    properties.multi = true;
409                }
410
411                if (properties.multi) {
412                    //multi selection facet search results
413                    _.each(facetChildren, function (val) {
414
415                        if (values.length > 1) {
416                            for (var i = 0, l = values.length; i < l; i++) {
417                                facetValue = inst.$el.find("[data-facetvalue]").filter(function () {
418                                    return decodeURIComponent($(this).attr('data-facetvalue').toLowerCase()) === values[i];
419                                });
420                                if (val === facetValue[0]) {
421                                    $(val).addClass('active-facet');
422                                    $(val).find('[type=checkbox]').prop('checked', true);
423                                }
424                            }
425                        }
426
427                        if (val === facetValue[0]) {
428                            $(val).addClass('active-facet');
429                            $(val).find('[type=checkbox]').prop('checked', true);
430                        }
431
432
433                    });
434                } else {
435                    //single selection facet search results filter allows only one facet type to be selected
436                    _.each(facetChildren, function (val) {
437                        if (val !== facetValue[0]) {
438                            $(val).removeClass('active-facet');
439                            $(val).find('[type=checkbox]').prop('checked', false);
440                            $(val).find('[type=checkbox] + label:after').css({ 'background': '#fff' });
441                        } else {
442                            $(val).addClass('active-facet');
443                        }
444                    });
445                }
446
447                //adds active class to group close button
448                facetClose.addClass('has-active-facet');
449            },
450            /**
451            * Manages search result filter highlight of threshold
452            * @param {Number} highlightThreshold value of threshold highlight
453            * @memberof module:facetResultsFilter.FacetResultsFilterView
454            * @alias module:facetResultsFilter.FacetResultsFilterView#handleThreshold
455            */
456            handleThreshold: function (highlightThreshold) {
457                var facets = this.$el.find('.facet-search-filter').children('p');
458
459                _.each(facets, function (single) {
460                    var $facet = $(single),
461                        $facetCount = $facet.find('.facet-count'),
462                        facetCount = $facetCount.data('facetcount');
463
464                    if (facetCount > highlightThreshold) {
465                        $facetCount.addClass('highlighted');
466                    }
467                });
468            }
469        });
470    /**
471    * For each search result component on a page creates instance of
472    * ["FacetResultsFilterModel"]{@link module:facetResultsFilter.FacetResultsFilterModel} and
473    * ["FacetResultsFilterView"]{@link module:facetResultsFilter.FacetResultsFilterView}
474    * @memberOf module:facetResultsFilter
475    * @alias module:facetResultsFilter.init
476    */
477    api.init = function () {
478        if ($("body").hasClass("on-page-editor")) {
479            return;
480        }
481
482        queryModel = XA.component.search.query;
483        apiModel = XA.component.search.ajax;
484        urlHelperModel = XA.component.search.url;
485
486        var facetResultsFilterList = $(".facet-single-selection-list:not(.initialized)");
487
488        _.each(facetResultsFilterList, function (elem) {
489            var $el = $(elem),
490                model = new FacetResultsFilterModel(),
491                view = new FacetResultsFilterView({ el: $el, model: model });
492        
493            $el.addClass("initialized");
494        });
495    };
496    /**
497    * Returns information about facet component
498    * @memberOf module:facetResultsFilter
499    * @alias module:facetResultsFilter.getFacetDataRequestInfo
500    * @returns {Array<FacetDataRequestInfo>} facet data needed for request
501    */
502    api.getFacetDataRequestInfo = function () {
503        var facetList = $(".facet-single-selection-list"),
504            result = [];
505
506        _.each(facetList, function (elem) {
507            var properties = $(elem).data().properties,
508                signatures = properties.searchResultsSignature.split(','),
509                i;
510
511            for (i = 0; i < signatures.length; i++) {
512                result.push({
513                    signature: signatures[i] === null ? "" : signatures[i],
514                    facetName: properties.f,
515                    endpoint: properties.endpoint,
516                    showMoreText: properties.showMoreText,
517                    showLessText: properties.showLessText,
518                    highlightBehaviour: properties.highlightBehaviour,
519                    s: properties.s,
520                    filterWithoutMe: !properties.collapseOnSelection
521                });
522            }
523        });
524
525        return result;
526    };
527
528    return api;
529
530}(jQuery, document));
531
532XA.register('facetResultsFilter', XA.component.search.facet.resultsfilter);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.