1/** 2 * Facet dropdown component functionality 3 * @module dropdown 4 * @param {jQuery} $ Instance of jQuery 5 * @param {Document} document dom document object 6 * @return {Object} list of methods for working with component dropdown 7*/ 8XA.component.search.facet.dropdown = (function ($, document) { 9 10 "use strict"; 11 /** 12 * This object stores all public api methods 13 * @type {Object.<Methods>} 14 * @memberOf module:dropdown 15 */ 16 var api = {}, 17 urlHelperModel, 18 queryModel 19 /** 20 * @name module:dropdown.FacetDropdownModel 21 * @constructor 22 * @augments Backbone.Model 23 */ 24 var FacetDropdownModel = XA.component.search.baseModel.extend( 25 /** @lends module:dropdown.FacetDropdownModel.prototype **/ 26 { 27 /** 28 * Default model options 29 * @default 30 */ 31 defaults: { 32 template: "<% _.forEach(results, function(result){" + 33 "%><option data-facetName=\"<%= result.Name !== '' ? encodeURIComponent(result.Name) : '_empty_' %>\" <%= result.Selected !== undefined ? 'selected' : '' %> ><%= result.Name !== '' ? sanitize(result.Name) : emptyText %> (<%= result.Count %>)</option><%" + 34 "}); %>", 35 dataProperties: {}, 36 blockNextRequest: false, 37 resultData: {}, 38 optionSelected: false, 39 sig: [] 40 }, 41 /** 42 * Listens to changes on facets and hash 43 * @listens module:XA.component.search.vent~event:facet-data-loaded 44 * @listens module:XA.component.search.vent~event:facet-data-filtered 45 * @listens module:XA.component.search.vent~event:facet-data-partial-filtered 46 * @listens module:XA.component.search.vent~event:hashChanged 47 */ 48 initialize: function () { 49 //Event to get data at the begining or when there are no hash parameters in the url - one request for all controls 50 XA.component.search.vent.on("facet-data-loaded", this.processData.bind(this, false)); 51 52 //Event to get filtered data 53 XA.component.search.vent.on("facet-data-filtered", this.processData.bind(this, false)); 54 55 //If the url contains a hash, and facet name control is clicked, then we have to listen for partial filtering 56 XA.component.search.vent.on("facet-data-partial-filtered", this.processData.bind(this, true)); 57 58 //Event after change of hash 59 XA.component.search.vent.on("hashChanged", this.updateComponent.bind(this)); 60 }, 61 /** 62 * Toggle value of blockNextRequest variable 63 * @memberof module:dropdown.FacetDropdownModel 64 * @alias module:dropdown.FacetDropdownModel#toggleBlockRequests 65 */ 66 toggleBlockRequests: function () { 67 var state = this.get("blockNextRequest"); 68 this.set(this.get("blockNextRequest"), !state); 69 }, 70 /** 71 * Processes data that comes as parameter update 72 * model and sorts facets 73 * @param {Object} data Data from server with facet values 74 * @memberof module:dropdown.FacetDropdownModel 75 * @alias module:dropdown.FacetDropdownModel#processData 76 */ 77 processData: function (partialFiltering, data) { 78 var inst = this, 79 hashObj = queryModel.parseHashParameters(window.location.hash), 80 sig = inst.get("sig"), 81 dataProperties = this.get("dataProperties"), 82 searchResultsSignature = dataProperties.searchResultsSignature.split(','), 83 sortOrder = dataProperties.sortOrder, 84 facet = dataProperties.f, 85 initialized, 86 updatedData, 87 facetItem, 88 facedData, 89 i, j; 90 91 for (j = 0; j < searchResultsSignature.length; j++) { 92 if (data.Facets.length > 0 && (data.Signature === searchResultsSignature[j]) || data.Signature === "" || data.Signature === null) { 93 facedData = _.find(data.Facets, function (f) { 94 return f.Key.toLowerCase() === facet.toLowerCase(); 95 }); 96 97 if (facedData === undefined) { 98 return; 99 } 100 101 for (i = 0; i < sig.length; i++) { 102 if (!jQuery.isEmptyObject(_.pick(hashObj, sig[i]))) { 103 if (hashObj[sig[i]] !== "") { 104 facetItem = _.where(facedData.Values, { Name: hashObj[sig[i]] }); 105 if (facetItem.length === 0) { 106 facetItem = _.where(facedData.Values, { Name: "" }); 107 } 108 if (facetItem.length > 0) { 109 facetItem[0].Selected = true; 110 inst.optionSelected = true; 111 } 112 } 113 } 114 } 115 116 if (searchResultsSignature.length > 1 && searchResultsSignature.indexOf(data.Signature) != -1) { 117 //two signatures, two search results components on the page, aggregating results from two search results component 118 updatedData = inst.get("resultData"); 119 initialized = inst.get('initialized_' + sig[j]); 120 121 if (!partialFiltering || (partialFiltering && !initialized)) { 122 for (const newItem in facedData.Values) { 123 var updated = false;
124 for (const existingItem in updatedData) { 125 if (facedData.Values[newItem].Name == updatedData[existingItem].Name) { 126 updatedData[existingItem].Count += facedData.Values[newItem].Count; 127 updated = true; 128 } 129 } 130 131 if (!updated) { 132 updatedData[Object.keys(updatedData).length] = facedData.Values[newItem] 133 } 134 } 135 136 //save that data for this signature has been initialized 137 inst.set('initialized_' + sig[j], true); 138 } 139 updatedData = inst.objectToArray(updatedData); 140 this.sortFacetArray(sortOrder, updatedData); 141 setTimeout(function () { 142 inst.set({ resultData: updatedData }); 143 }); 144 } else { 145 this.sortFacetArray(sortOrder, facedData.Values); 146 inst.set({ resultData: facedData.Values }); 147 } 148 } 149 } 150 }, 151 /** 152 * Sets option selected value to model based on hash 153 * @param {Object} hash Hash stored as an object 154 * @memberof module:dropdown.FacetDropdownModel 155 * @alias module:dropdown.FacetDropdownModel#updateComponent 156 */ 157 updateComponent: function (hash) { 158 var sig = this.get("sig"), i, facetPart; 159 160 for (i = 0; i < sig.length; i++) { 161 facetPart = sig[i].toLowerCase(); 162 if (hash.hasOwnProperty(facetPart) && hash[facetPart] !== "") { 163 this.set({ optionSelected: true }); 164 } else { 165 this.set({ optionSelected: false }); 166 } 167 } 168 } 169 }); 170 /** 171 * @name module:dropdown.FacetDropdownView 172 * @constructor 173 * @augments Backbone.View 174 */ 175 var FacetDropdownView = XA.component.search.baseView.extend( 176 /** @lends module:dropdown.FacetDropdownView.prototype **/ 177 178 { 179 /** 180 * Initially sets data to model and watches events on which 181 * view should be updated 182 * @listens module:dropdown.FacetDropdownModel~event:change 183 * @memberof module:dropdown.FacetDropdownView 184 * @alias module:dropdown.FacetDropdownView#initialize 185 */ 186 initialize: function () { 187 var dataProperties = this.$el.data(); 188 this.properties = dataProperties.properties; 189 190 if (this.model) { 191 this.model.set({ dataProperties: this.properties }); 192 this.model.set("sig", this.translateSignatures(this.properties.searchResultsSignature, this.properties.f)); 193 } 194 195 this.model.on("change", this.render, this); 196 }, 197 /** 198 * list of events for Backbone View 199 * @memberof module:dropdown.FacetDropdownView 200 * @alias module:dropdown.FacetDropdownView#events 201 */ 202 events: { 203 "change .facet-dropdown-select": "updateFacet", 204 "click .bottom-remove-filter, .clear-filter": "clearFilter" 205 }, 206 /** 207 * Calculates value of optionSelected value in model and calls 208 * ["updateSignaturesHash"]{@link module dropdown.FacetDropdownView#updateSignaturesHash} 209 * inside of ["updateHash"]{@link module:searchQuery.updateHash} 210 * @memberof module:dropdown.FacetDropdownView 211 * @alias module:dropdown.FacetDropdownView#updateFacet 212 */ 213 updateFacet: function (param) { 214 var $selectedOption = this.$el.find(".facet-dropdown-select").find("option:selected"), 215 searchModels = typeof XA.component.search !== "undefined" ? XA.component.search.results.searchResultModels : [], 216 facetName = decodeURIComponent($selectedOption.data("facetname")), 217 sig = this.model.get("sig"), 218 i; 219 220 if (facetName === "") { 221 this.model.set({ optionSelected: false }); 222 } else { 223 this.model.set({ optionSelected: true }); 224 } 225 226 //reset load more after facet change 227 for (i = 0; i < searchModels.length; i++) { 228 if (searchModels[i].get("dataProperties").sig === this.model.get("dataProperties").searchResultsSignature) { 229 searchModels[i].set("loadMoreOffset", 0); 230 } 231 } 232
233 queryModel.updateHash(this.updateSignaturesHash(sig, facetName, {})); 234 }, 235 /** 236 * Renders view 237 * @memberof module:dropdown.FacetDropdownView 238 * @alias module:dropdown.FacetDropdownView#render 239 */ 240 render: function () { 241 var inst = this, 242 resultData = inst.model.get("resultData"), 243 dropdown = this.$el.find(".facet-dropdown-select"), 244 emptyValueText = inst.model.get('dataProperties').emptyValueText, 245 facetClose = this.$el.find(".facet-heading > span"), 246 notSelectedOption = dropdown.find("option:first"), 247 notSelectedOptionEntry = $("<option />"), 248 template = _.template(inst.model.get("template")), 249 templateResult = template({ results: resultData, emptyText: emptyValueText, sanitize: XA.security.sanitize }); 250 251 this.manageVisibilityByData(this.$el, resultData) 252 notSelectedOptionEntry.text(notSelectedOption.text()); 253 notSelectedOptionEntry.data("facetname", ""); 254 255 if (this.model.get("optionSelected")) { 256 facetClose.addClass("has-active-facet"); 257 } else if (notSelectedOption.data("facetname") === "") { 258 facetClose.removeClass("has-active-facet"); 259 } 260 261 dropdown.empty().append(notSelectedOptionEntry).append(templateResult); 262 }, 263 /** 264 * Clears selected filters and updates hash by calling 265 * ["updateHash"]{@link module:searchQuery.updateHash} 266 * @memberof module:dropdown.FacetDropdownView 267 * @alias module:dropdown.FacetDropdownView#clearFilter 268 */ 269 clearFilter: function () { 270 var dropdown = this.$el.find(".facet-dropdown-select"), 271 facetClose = this.$el.find(".facet-heading > span"), 272 sig = this.model.get("sig"); 273 274 queryModel.updateHash(this.updateSignaturesHash(sig, "", {})); 275 this.model.set({ optionSelected: false }); 276 facetClose.removeClass("has-active-facet"); 277 dropdown.val(dropdown.find("option:first").val()); 278 } 279 }); 280 /** 281 * For each search dropdown component on a page creates instance of 282 * ["FacetDropdownModel"]{@link module:dropdown.FacetDropdownModel} and 283 * ["FacetDropdownView"]{@link module:dropdown.FacetDropdownView} 284 * @memberOf module:dropdown 285 * @alias module:dropdown.init 286 */ 287 288 api.init = function () { 289 if ($("body").hasClass("on-page-editor")) { 290 return; 291 } 292 293 queryModel = XA.component.search.query; 294 urlHelperModel = XA.component.search.url; 295 296 var facetDropdownList = $(".facet-dropdown:not(.initialized)"); 297 298 _.each(facetDropdownList, function (elem) { 299 var $el = $(elem), 300 view = new FacetDropdownView({ el: $el, model: new FacetDropdownModel() }); 301 302 $el.addClass("initialized"); 303 }); 304 }; 305 /** 306 * 307 * @memberOf module:dropdown 308 * @alias module:dropdown.getFacetDataRequestInfo 309 * @returns {Array<FacetDataRequestInfo>} facet data needed for request 310 */ 311 api.getFacetDataRequestInfo = function () { 312 var facetDropdownList = $(".facet-dropdown"), 313 result = []; 314 315 _.each(facetDropdownList, function (elem) { 316 var properties = $(elem).data().properties, 317 signatures = properties.searchResultsSignature.split(','), 318 i; 319 320 for (i = 0; i < signatures.length; i++) { 321 result.push({ 322 signature: signatures[i] === null ? "" : signatures[i], 323 facetName: properties.f, 324 endpoint: properties.endpoint, 325 filterWithoutMe: true 326 }); 327 } 328 }); 329 330 return result; 331 }; 332 333 return api; 334 335}(jQuery, document)); 336 337XA.register("facetDropdown", XA.component.search.facet.dropdown);
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.