1 2/** 3 * Facet summary component functionality 4 * @module facetSummary 5 * @param {jQuery} $ Instance of jQuery 6 * @param {Document} document dom document object 7 * @return {Object} list of methods for working with component facet summary 8*/ 9XA.component.search.facet.summary = (function ($, document) { 10 /** 11 * This object stores all public api methods 12 * @type {Object.<Methods>} 13 * @memberOf module:facetSummary 14 */ 15 var api = {}, 16 urlHelperModel = XA.component.search.url, 17 queryModel = XA.component.search.query, 18 apiModel = XA.component.search.ajax, 19 facetName = "summary"; 20 21 /** 22 * @name module:facetSummary.FacetSummaryModel 23 * @constructor 24 * @augments Backbone.Model 25 */ 26 var FacetSummaryModel = Backbone.Model.extend( 27 /** @lends module:facetSummary.FacetSummaryModel.prototype **/ 28 29 { 30 /** 31 * Default model options 32 * @default 33 * @param {Object} properties properties of ajax call and callback 34 */ 35 defaults: { 36 dataProperties: {}, 37 sig: [], 38 hash: '' 39 }, 40 /** 41 * Listens to changes on facets and hash 42 * @listens module:XA.component.search.vent~event:facet-data-loaded 43 * @listens module:XA.component.search.vent~event:facet-data-filtered 44 * @listens module:XA.component.search.vent~event:facet-data-partial-filtered 45 * @listens module:XA.component.search.vent~event:hashChanged 46 */ 47 initialize: function () { 48 var that = this; 49 this.set('resultData', {}) 50 //Event to get data at the begining or in case that there are no hash parameters in the url - one request for all controls 51 XA.component.search.vent.on("facet-data-loaded", that.processData.bind(that)); 52 // //If the url hash contains this control facet name (someone clicked this control), then we have to listen for partial filtering 53 XA.component.search.vent.on("facet-data-partial-filtered", that.processData.bind(that)); 54 // //When we are not filtering by this control (not clicked) 55 XA.component.search.vent.on("facet-data-filtered", that.processData.bind(that)); 56 57 }, 58 /** 59 * Finds all facets that have same name as facetName in Control Data and return them 60 * @param {Object} facetData 61 * @param {*} controlData 62 * @returns {Array} Filtered facet data 63 * @memberof module:facetSummary.FacetSummaryModel 64 * @alias module:facetSummary.FacetSummaryModel.requestFacetData 65 */ 66 requestFacetData: function (facetData, controlData) { 67 return _.find(facetData, function (f) { 68 return f.Key.toLowerCase() === controlData.facetName.toLowerCase(); 69 }); 70 }, 71 /** 72 * Concatenates signature and facet name in order to build proper hash parameter name 73 * @param {*} rawSignature 74 * @param {*} f 75 */ 76 translateSignatures: function (rawSignature, f) { 77 var signatures, i; 78 79 f = f.toLowerCase(); 80 81 if (typeof rawSignature === "undefined" || rawSignature === null) { 82 return [f]; 83 } 84 85 signatures = rawSignature.split(','); 86 87 if (rawSignature === "") { 88 return [f]; 89 } else { 90 for (i = 0; i < signatures.length; i++) { 91 signatures[i] = signatures[i] + "_" + f; 92 } 93 return signatures; 94 } 95 }, 96 /** 97 * Processes data that comes as parameter. Update 98 * model and store all facets that are active and have 99 * same signature 100 * @param {Object} data Data from server with facet values
101 * @memberof module:facetSummary.FacetSummaryModel 102 * @alias module:facetSummary.FacetSummaryModel.processData 103 */ 104 processData: function (data) { 105 var hashObj = queryModel.parseHashParameters(window.location.hash), 106 requestFacedData, 107 that = this, 108 displayName, 109 facetData = [], requestData = {}, facetControl, control; 110 var facets = XA.component.search.facet; 111 112 for (facetControl in facets) { 113 control = XA.component.search.facet[facetControl]; 114 if (typeof (control.getFacetDataRequestInfo) === "function") { 115 facetData = control.getFacetDataRequestInfo(); 116 for (var facetIndex = 0; facetIndex < facetData.length; facetIndex++) { 117 var controlData = facetData[facetIndex]; 118 var signaturesArray = controlData.signature.split(','); 119 for (var signatureIndex = 0; signatureIndex < signaturesArray.length; signatureIndex++) { 120 var signature = signaturesArray[signatureIndex]; 121 if (data.Signature == signature && that.get('sig') == signature) { 122 requestFacedData = that.requestFacetData(data.Facets, controlData); 123 if (typeof requestFacedData !== "undefined") { 124 displayName = requestFacedData.Name; 125 var currResults = this.get('resultData'); 126 var facetHashNames = that.translateSignatures(signature, requestFacedData.Key.toLowerCase()) 127 for (var facetHashNameIndex = 0; facetHashNameIndex < facetHashNames.length; facetHashNameIndex++) { 128 var facetHashName = facetHashNames[facetHashNameIndex]; 129 if (hashObj[facetHashName]) { 130 currResults[displayName] = { 131 "value": hashObj[facetHashName], 132 "key": requestFacedData.Key, 133 "signature": data.Signature, 134 "facetHashName": facetHashName 135 } 136 } else { 137 delete currResults[displayName]; 138 } 139 } 140 this.set('resultData', currResults); 141 this.trigger('change', this) 142 143 } 144 } 145 } 146 147 } 148 } 149 } 150 } 151 }); 152 /** 153 * @name module:facetSummary.FacetSummaryView 154 * @constructor 155 * @augments Backbone.View 156 */ 157 var FacetSummaryView = XA.component.search.baseView.extend( 158 /** @lends module:facetSummary.FacetSummaryView.prototype **/ 159 160 { 161 tagName: "div", 162 className: "facet-search-summary", 163 template: 164 "<% if(Object.keys(resultData).length){ %> <div class='facet-summary-wrapper clearfix'>" + 165 "<% _.forEach(resultData, function(obj,key){ %>" + 166 "<div class='active-facet-summary-element' data-hash='" + "<%=obj.facetHashName%>" + "' data-key='" + "<%=obj.key%>" + "' data-signature='" + "<%=obj.signature%>" + "'>" + 167 "<span class='facet-summary-name'><%= key %>:</span> " + "<%= renderFacetValues(obj.value) %>" + 168 "</div>" + 169 "<% }); %>" + 170 "</div><% }%>", 171 172 renderFacetValues: function (value) { 173 var params = value.split("||") 174 175 return params.map(function (p) { 176 var template = '<div class=\'facet-summary-value\' data-value=' + encodeURIComponent(p) + '>' 177 + '<p>' + p + '</p>' 178 + '<span class=\'removeFacetValue\'>x</span>' 179 + '</div>' 180 181 return template; 182 }).join(' ') 183 }, 184 /** 185 * Initially sets data to model and watches events on which 186 * view should be updated 187 * @listens module:facetSummary.FacetSummaryView~event:change 188 * @memberof module:facetSummary.FacetSummaryView 189 * @alias module:facetSummary.FacetSummaryView#initialize 190 */ 191 clearButtons: function (event) { 192 var $target = $(event.currentTarget), 193 $facetSummary = $target.closest(".facet-summary"); 194 $facetSummary.find('.clear-all-active-facets').trigger('click'); 195 196 }, 197 /** 198 * Initializes the view by assigning events to the buttons and rendering component 199 * @memberof module:facetSummary.FacetSummaryView 200 * @alias module:facetSummary.FacetSummaryView#events 201 */ 202 initialize: function () { 203 var dataProperties = this.$el.data(), 204 that = this, 205 properties = dataProperties.properties, 206 signatures; 207 Backbone.$('.facet-summary .clear-filter').on('click', function (event) { 208 that.clearButtons(event); 209 that.updateUrlWithoutHash(); 210 211 }) 212 Backbone.$('.facet-summary .bottom-remove-filter').on('click', function (event) { 213 that.clearButtons(event); 214 that.updateUrlWithoutHash(); 215 216 }) 217 signatures = this.translateSignatures(properties.searchResultsSignature, facetName); 218 this.model.set("sig", properties.searchResultsSignature); 219 dataProperties = this.$el.data("properties"), 220 this.model.on("change", this.render, this); 221 this.render(); 222 }, 223 /** 224 * list of events for Backbone View 225 * @memberof module:facetSummary.FacetSummaryView 226 * @alias module:facetSummary.FacetSummaryView#events 227 */ 228 events: { 229 'click .removeFacetValue': 'removeFacetValue', 230 'click .clear-filter': 'clearAllActiveFacets', 231 'click .bottom-remove-filter button': 'clearAllActiveFacets' 232 }, 233 updateUrlWithoutHash: function () { 234 // var currentUrl = window.location.href.split('#')[0]; 235 // history.replaceState({}, document.title, currentUrl); 236 var currentUrl = window.location.href; 237 var hashIndex = currentUrl.indexOf('#'); 238 239 if (hashIndex !== -1) { 240 var hashPart = currentUrl.substring(hashIndex); 241 242 if (hashPart === "#salon-locator_g=true") { 243 return; 244 } else if (hashPart.indexOf("#salon-locator_g=true") !== -1) { 245 var newHashPart = "#salon-locator_g=true"; 246 history.replaceState({}, document.title, currentUrl.split('#')[0] + newHashPart); 247 } else { 248 history.replaceState({}, document.title, currentUrl.split('#')[0]); 249 } 250 } 251 }, 252 /** 253 * Prepares hash string based on hash object 254 * @param {Object} hashObj Object created from hash parameters 255 * @memberof module:facetSummary.FacetSummaryView 256 * @alias module:facetSummary.FacetSummaryView#createHash 257 * @returns {String}
257 hash as a string 258 */ 259 createHash: function (hashObj) { 260 var result = "#"; 261 for (option in hashObj) { 262 result += option + "=" + hashObj[option] + "&"; 263 } 264 return result; 265 }, 266 /** 267 * Updates current location 268 * @param {Object} hashObj 269 */ 270 updateLocation: function (hashObj) { 271 XA.component.search.query.updateHash(hashObj); 272 }, 273 /** 274 * Returns facet params 275 * @param {Jquery} el jquery event target element 276 */ 277 getFacetParams: function (el) { 278 var summaryEl = $(el).closest(".active-facet-summary-element"); 279 280 return { 281 key: summaryEl.data('key'), 282 signature: summaryEl.data('signature') 283 } 284 }, 285 /** 286 * Clears all active facets that are stored under summary component 287 * @memberof module:facetSummary.FacetSummaryView 288 * @alias module:facetSummary.FacetSummaryView#clearAllActiveFacets 289 */ 290 clearAllActiveFacets: function () { 291 var self = this; 292 var hashObj = queryModel.parseHashParameters(window.location.hash); 293 294 this.$el.find('.active-facet-summary-element').each(function () { 295 var facetParams = self.getFacetParams($(this)); 296 var facetHashNames = self.translateSignatures(facetParams.signature, facetParams.key) 297 298 _.each(facetHashNames, function (facetHashName) { 299 hashObj[facetHashName] = ""; 300 }) 301 }) 302 303 self.updateLocation(hashObj); 304 }, 305 /** 306 * Removes selected facet value and updates hash 307 * @param {Event} event Event object where stored clicked element 308 */ 309 removeFacetValue: function (event) { 310 var facetValueElem = $(event.currentTarget).closest(".facet-summary-value") 311 312 var selectedFacetValue = decodeURIComponent(facetValueElem.data('value')) 313 var facetParams = this.getFacetParams(event.currentTarget) 314 315 var hashObj = queryModel.parseHashParameters(window.location.hash); 316 var facetHashNames = this.translateSignatures(facetParams.signature, facetParams.key) 317 318 var removeSelectedValue = function (facetHashName) { 319 hashObj[facetHashName] = hashObj[facetHashName] 320 .split("||") 321 .filter(function (val) { 322 return val !== selectedFacetValue 323 }) 324 .join("||"); 325 } 326 327 _.each(facetHashNames, removeSelectedValue) 328 329 this.updateLocation(hashObj); 330 }, 331 /** 332 * Renders view 333 * @memberof module:facetSummary.FacetSummaryView 334 * @alias module:facetSummary.FacetSummaryView#render 335 */ 336 render: function () { 337 var inst = this, 338 resultData = this.model.get("resultData"), 339 template, templateResult; 340 var dataProperties = this.$el.data(); 341 this.properties = dataProperties.properties; 342 this.manageVisibilityByData(this.$el, resultData) 343 if (this.model) { 344 this.model.set({ dataProperties: this.properties }); 345 this.model.set("sig", this.properties.searchResultsSignature); 346 } 347 //checks whether page is opened from disc - if yes then we are in Creative Exchange mode 348 if (window.location.href.startsWith("file://")) { 349 return; 350 } 351 352 if (resultData !== undefined) { 353 template = _.template(this.template, { 354 imports: { renderFacetValues: this.renderFacetValues } 355 }); 356 357 templateResult = template({ 358 "resultData": resultData 359 }); 360 } 361 362 inst.$el.find(".facet-summary-placeholder").html(templateResult); 363 } 364 365 }); 366 /** 367 * For each search dropdown component on a page creates instance of
368 * ["FacetSummaryModel"]{@link module:facetSummary.FacetSummaryModel} and 369 * ["FacetSummaryView"]{@link module:facetSummary.FacetSummaryView} 370 * @memberOf module:facetSummary 371 * @alias module:facetSummary.init 372 */ 373 api.init = function () { 374 if ($("body").hasClass("on-page-editor")) { 375 return; 376 } 377 378 queryModel = XA.component.search.query; 379 apiModel = XA.component.search.ajax; 380 urlHelperModel = XA.component.search.url; 381 382 var facetSummary = $(".facet-summary:not(.initialized)"); 383 384 _.each(facetSummary, function (elem) { 385 var $el = $(elem), 386 model = new FacetSummaryModel(), 387 view = new FacetSummaryView({ el: $el, model: model }); 388 389 $el.addClass("initialized"); 390 }); 391 }; 392 393 return api; 394 395}(jQuery, document)); 396 397XA.register('facetSummary', XA.component.search.facet.summary);
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.