1(function ($) { 2 3 /** 4 * Class containing functionality for Facet API. 5 */ 6 Drupal.ajax_facets = {}; 7 // Store current query value. 8 Drupal.ajax_facets.queryState = null; 9 // State of each facet. 10 Drupal.ajax_facets.facetQueryState = null; 11 // Determine if it is first page load. Will be reset in Drupal.ajax_facets.initHistoryState. 12 Drupal.ajax_facets.firstLoad = true; 13 // Settings of each ajax facet. 14 Drupal.ajax_facets.facetsSettings = {}; 15 // Buttons for ajax facets. 16 Drupal.ajax_facets.ajax_facets_buttons = false; 17 // Force update of the results 18 Drupal.ajax_facets.force_update_results = false; 19 // Tooltip timeout handler. 20 Drupal.ajax_facets.tooltipTimeout; 21 // Name of the facet which was clicked. 22 Drupal.ajax_facets.current_facet_name = undefined; 23 24 // You can use it for freeze facet form elements while ajax is processing. 25 Drupal.ajax_facets.beforeAjaxCallbacks = {}; 26 27 // You can use it to make actions after the ajax has updated the content. 28 Drupal.ajax_facets.afterContentUpdateCallbacks = {}; 29 30 Drupal.ajax_facets.beforeAjax = function () { 31 $.each(Drupal.ajax_facets.beforeAjaxCallbacks, function () { 32 if ($.isFunction(this)) { 33 this(); 34 } 35 }); 36 }; 37 38 Drupal.ajax_facets.afterContentUpdate = function (ajax, response) { 39 Drupal.ajax_facets.force_update_results = false; 40 $.each(Drupal.ajax_facets.afterContentUpdateCallbacks, function () { 41 if ($.isFunction(this)) { 42 this(ajax, response); 43 } 44 }); 45 }; 46 47 Drupal.behaviors.ajax_facets = { 48 attach: function (context, settings) { 49 if (!Drupal.ajax_facets.queryState) { 50 // Add property for views and set data about facet values. 51 Drupal.ajax_facets.queryState = { 52 'views': new Array(), 53 'f': settings.facetapi.defaultQuery ? settings.facetapi.defaultQuery : [] 54 }; 55 56 // We will send original search path to server to get back proper reset links. 57 if (settings.facetapi.searchPath) { 58 Drupal.ajax_facets.queryState['searchPath'] = settings.facetapi.searchPath; 59 } 60 if (settings.facetapi.index_id) { 61 Drupal.ajax_facets.queryState['index_id'] = settings.facetapi.index_id; 62 } 63 // Check view name and display name for all views. 64 if (settings.facetapi.views) { 65 $.each(settings.facetapi.views, function(key, view) { 66 Drupal.ajax_facets.queryState['views'][key] = {}; 67 // Get view dom id. 68 var viewDomId = Drupal.ajax_facets.getViewDomId(settings, key); 69 if (viewDomId) { 70 // Take settings from view if it works by AJAX. 71 if (Drupal.views && Drupal.views.instances['views_dom_id:' + viewDomId]) { 72 $.extend(Drupal.ajax_facets.queryState['views'][key], Drupal.views.instances['views_dom_id:' + viewDomId].settings); 73 } 74 // If this view doesn't use ajax. 75 else { 76 Drupal.ajax_facets.queryState['views'][key] = { 77 'view_dom_id': viewDomId, 78 'view_name': view.view_name, 79 'view_display_id': view.view_display_id 80 }; 81 // Respect view arguments. 82 var nameDisplay = view.view_name + ':' + view.view_display_id; 83 if (settings.facetapi.view_args[nameDisplay]) { 84 Drupal.ajax_facets.queryState['views'][key]['view_args'] = settings.facetapi.view_args[nameDisplay]; 85 } 86 // Respect view path. 87 if (settings.facetapi.view_path[nameDisplay]) { 88 Drupal.ajax_facets.queryState['views'][key]['view_path'] = settings.facetapi.view_path[nameDisplay]; 89 } 90 } 91 } 92 93 // Respect arguments in exposed form. 94 if (view.view_name && view.view_display_id) { 95 var nameDisplay = view.view_name + ':' + view.view_display_id; 96 if (settings.facetapi.exposed_input[nameDisplay]) { 97 $.extend(Drupal.ajax_facets.queryState, settings.facetapi.exposed_input[nameDisplay]); 98 } 99 } 100 }); 101 } 102 103 if (settings.facetapi.facet_field) { 104 Drupal.ajax_facets.queryState['facet_field'] = settings.facetapi.facet_field; 105 } 106 } 107 108 // Iterates over facet settings, applies functionality like the "Show more" 109 // links for block realm facets. 110 // @todo We need some sort of JS API so we don't have to make decisions 111 // based on the realm. 112 if (settings.facetapi) { 113 for (var index in settings.facetapi.facets) { 114 Drupal.ajax_facets.bindResetLink(settings.facetapi.facets[index].id, index, settings); 115 116 // Checkboxes. 117 if (settings.facetapi.facets[index].widget == 'facetapi_ajax_checkboxes') { 118 $('#' + settings.facetapi.facets[index].id + '-wrapper input.facet-multiselect-checkbox:not(.processed)').change( 119 [settings.facetapi.facets[index]], 120 Drupal.ajax_facets.processCheckboxes 121 ).addClass('processed'); 122 } 123 124 // Selectboxes. 125 if (settings.facetapi.facets[index].widget == 'facetapi_ajax_select') { 126 $('#' + settings.facetapi.facets[index].id + '-wrapper select:not(.processed)').each(function () { 127 $(this).change( 128 [settings.facetapi.facets[index]], 129 Drupal.ajax_facets.processSelectbox 130 ).addClass('processed'); 131 }); 132 } 133 134 // Links. 135 if (settings.facetapi.facets[index].widget == 'facetapi_ajax_links') { 136 $('#' + settings.facetapi.facets[index].id + '-wrapper a:not(.processed)').each(function () { 137 $(this).click( 138 [settings.facetapi.facets[index]], 139 Drupal.ajax_facets.processLink 140 ).addClass('processed'); 141 }); 142 } 143 144 // Ranges. 145 if (settings.facetapi.facets[index].widget == 'facetapi_ajax_ranges') { 146 $('#' + settings.facetapi.facets[index].id + '-wrapper div.slider-wrapper:not(.processed)').each(function () { 147 var $sliderWrapper = $(this); 148 // Fix JS exception when doing mixed type search - Added by James 2021-10-18 149 try{ 150 $sliderWrapper.slider({ 151 range: true, 152 min: parseFloat($sliderWrapper.data('min')), 153 max: parseFloat($sliderWrapper.data('max')), 154 values: [ parseFloat($sliderWrapper.data('min-val')), parseFloat($sliderWrapper.data('max-val')) ], 155 stop: function( event, ui ) { 156 Drupal.ajax_facets.processSlider($sliderWrapper, ui.values[0], ui.values[1]); 157 } 158 }).addClass('processed'); 159 } 160 catch{ 161 $sliderWrapper.addClass('processed'); 162 } 163 164 // Bind input fields. 165 var $sliderWrapperParent = $sliderWrapper.parent(); 166 $sliderWrapperParent.find("input[type='text']").each(function() { 167 $(this).change(function() { 168 var min = parseFloat($sliderWrapperParent.find('.ajax-facets-slider-amount-min').val()); 169 var max = parseFloat($sliderWrapperParent.find('.ajax-facets-slider-amount-max').val()); 170 // If values are numeric and min less than max. 171 if (isFinite(min) && isFinite(max) && min < max) { 172 $sliderWrapper.slider('values', 0, min); 173 $sliderWrapper.slider('values', 1, max); 174 Drupal.ajax_facets.processSlider($sliderWrapper, min, max); 175 } 176 }); 177 }); 178 179 }); 180 } 181 182 if (null != settings.facetapi.facets[index].limit) { 183 // Applies soft limit to the list. 184 if (Drupal.facetapi) { 185 Drupal.facetapi.applyLimit(settings.facetapi.facets[index]); 186 } 187 } 188 189 // Save settings for each facet by name. 190 Drupal.ajax_facets.facetsSettings[settings.facetapi.facets[index].facetName] = settings.facetapi.facets[index]; 191 } 192 } 193 194 // Hide blocks with ajax-facets-empty-behavior. 195 $('.ajax-facets-empty-behavior').parents('.block--ajax_facets').hide(); 196 197 // Add facets tooltip. 198 $('body').once(function () { 199 $(this).append('<div id="ajax-facets-tooltip"><span></span></div>'); 200 }); 201 } 202 }; 203 204 Drupal.behaviors.ajax_facets_block = { 205 attach: function (context, settings) { 206 // Reset all link. 207 $('.ajax-facets-reset-all-link:not(.processed)').bind('click', function (e) { 208 e.preventDefault(); 209 210 // Clear all facets from queryState facets array. 211 Drupal.ajax_facets.queryState.f = []; 212 213 // Send ajax query with first found facet. 214 Drupal.ajax_facets.force_update_results = true; 215 Drupal.ajax_facets.sendAjaxQuery({ 216 pushStateNeeded: true, 217 searchResultsNeeded: true 218 }); 219 }).addClass('processed'); 220 221 var $submitLink = $('.ajax-facets-submit-all-link:not(.processed)'); 222 if ($submitLink.length) { 223 // Submit link found. 224 Drupal.ajax_facets.ajax_facets_buttons = true; 225 // Submit all link. 226 $submitLink.bind('click', function (e) { 227 e.preventDefault(); 228 // Send ajax query with first found facet. 229 Drupal.ajax_facets.force_update_results = true; 230 Drupal.ajax_facets.sendAjaxQuery({ 231 pushStateNeeded: true, 232 searchResultsNeeded: true 233 }); 234 }).addClass('processed'); 235 } 236 } 237 }; 238 239 240 Drupal.ajax_facets.bindResetLink = function (facetWrapperId, index, settings) { 241 var facet_values = Drupal.ajax_facets.getFacetValues(); 242 var $facetWrapper = $('#' + facetWrapperId + '-wrapper'); 243 if (facet_values[settings.facetapi.facets[index]['facetName']]) { 244 $facetWrapper.find('a.reset-link').show(); 245 } else { 246 $facetWrapper.find('a.reset-link').hide(); 247 } 248 249 $facetWrapper.find('a.reset-link:not(".processed")').addClass('processed').click(function (event) { 250 var $facet = $(this).parent().find('[data-facet-name]').first(); 251 var facetName = $facet.data('facet-name'); 252 Drupal.ajax_facets.excludeCurrentFacet(facetName); 253 Drupal.ajax_facets.sendAjaxQuery({ 254 pushStateNeeded: true, 255 searchResultsNeeded: true 256 }); 257 event.preventDefault(); 258 }); 259 }; 260 261 /** 262 * Callback for onClick event for widget selectbox. 263 */ 264 Drupal.ajax_facets.processSelectbox = function (event) { 265 var $this = $(this), 266 facetName = $this.data('facet-name'); 267 // Init history. 268 Drupal.ajax_facets.initHistoryState($this); 269 // If facets are already defined in queryState. 270 if (Drupal.ajax_facets.queryState['f']) { 271 // Exclude all values for this facet from query. 272 Drupal.ajax_facets.excludeCurrentFacet(facetName); 273 274 /* Default value. */ 275 if ($this.find(":selected").val() == '_none') { 276 delete Drupal.ajax_facets.queryState['f'][Drupal.ajax_facets.queryState['f'].length]; 277 } else { 278 Drupal.ajax_facets.queryState['f'][Drupal.ajax_facets.queryState['f'].length] = facetName + ':' + $this.find(":selected").val(); 279 } 280 } 281 282 Drupal.ajax_facets.sendAjaxQuery({ 283 pushStateNeeded: !Drupal.ajax_facets.ajax_facets_buttons, 284 searchResultsNeeded: !Drupal.ajax_facets.ajax_facets_buttons 285 }, $this); 286 }; 287 288 /** 289 * Callback for onClick event for widget checkboxes. 290 */ 291 Drupal.ajax_facets.processCheckboxes = function (event) { 292 var $this = $(this), 293 facetName = $this.data('facet-name'), 294 name_value = facetName + ':' + $this.attr('data-facet-value'),// $.data can round decimal values like 4.0 to 4, avoid it. 295 rawFacetName = $this.data('raw-facet-name'); 296 // Init history. 297 Drupal.ajax_facets.initHistoryState($this); 298 // If facets are already defined in queryState. 299 if (Drupal.ajax_facets.queryState['f']) { 300 var queryNew = []; 301 if ($this.is(':checked')) { 302 var addCurrentParam = true; 303 for (var index in Drupal.ajax_facets.queryState['f']) { 304 if (Drupal.ajax_facets.queryState['f'].hasOwnProperty(index)) { 305 // If we already have this value in queryState. 306 if (Drupal.ajax_facets.queryState['f'][index] == name_value) { 307 addCurrentParam = false;
308 } 309 } 310 } 311 // Add new value if need. 312 if (addCurrentParam) { 313 // Exclude all other values of this facet from query. 314 if (Drupal.ajax_facets.facetsSettings[rawFacetName].limit_active_items) { 315 Drupal.ajax_facets.excludeCurrentFacet(facetName); 316 } 317 Drupal.ajax_facets.queryState['f'][Drupal.ajax_facets.queryState['f'].length] = name_value; 318 } 319 } 320 // If we unset filter, remove it from query. 321 else { 322 for (var index in Drupal.ajax_facets.queryState['f']) { 323 if (Drupal.ajax_facets.queryState['f'].hasOwnProperty(index)) { 324 if (Drupal.ajax_facets.queryState['f'][index] != name_value) { 325 queryNew[queryNew.length] = Drupal.ajax_facets.queryState['f'][index]; 326 } 327 } 328 } 329 Drupal.ajax_facets.queryState['f'] = queryNew; 330 } 331 } 332 333 // Fix to remove duplicate facet params - Added by James 2021-10-18 334 Drupal.ajax_facets.queryState['f'] = [...new Set(Drupal.ajax_facets.queryState['f'])].sort(); 335 336 Drupal.ajax_facets.sendAjaxQuery({ 337 pushStateNeeded: !Drupal.ajax_facets.ajax_facets_buttons, 338 searchResultsNeeded: !Drupal.ajax_facets.ajax_facets_buttons 339 }, $this); 340 }; 341 342 /** 343 * Callback for onClick event for widget links. 344 */ 345 Drupal.ajax_facets.processLink = function (event) { 346 var $this = $(this), 347 facetName = $this.data('facet-name'), 348 name_value = facetName + ':' + $this.attr('data-facet-value'),// $.data can round decimal values like 4.0 to 4, avoid it. 349 rawFacetName = $this.data('raw-facet-name'); 350 // Init history. 351 Drupal.ajax_facets.initHistoryState($this); 352 // If facets are already defined in queryState. 353 if (Drupal.ajax_facets.queryState['f']) { 354 var queryNew = []; 355 /* Handle value - deactivate. */ 356 if ($this.hasClass('facetapi-active')) { 357 for (var index in Drupal.ajax_facets.queryState['f']) { 358 if (Drupal.ajax_facets.queryState['f'].hasOwnProperty(index)) { 359 if (Drupal.ajax_facets.queryState['f'][index] !== name_value) { 360 queryNew[queryNew.length] = Drupal.ajax_facets.queryState['f'][index]; 361 } 362 } 363 } 364 Drupal.ajax_facets.queryState['f'] = queryNew; 365 } 366 /* Handle value - activate. */ 367 else if ($this.hasClass('facetapi-inactive')) { 368 var addCurrentParam = true; 369 for (var index in Drupal.ajax_facets.queryState['f']) { 370 if (Drupal.ajax_facets.queryState['f'].hasOwnProperty(index)) { 371 if (Drupal.ajax_facets.queryState['f'][index] === name_value) { 372 addCurrentParam = false; 373 } 374 } 375 } 376 if (addCurrentParam) { 377 // Exclude all other values of this facet from query. 378 if (Drupal.ajax_facets.facetsSettings[rawFacetName].limit_active_items) { 379 Drupal.ajax_facets.excludeCurrentFacet(facetName); 380 } 381 Drupal.ajax_facets.queryState['f'][Drupal.ajax_facets.queryState['f'].length] = name_value; 382 } 383 } 384 } 385 386 Drupal.ajax_facets.sendAjaxQuery({ 387 pushStateNeeded: !Drupal.ajax_facets.ajax_facets_buttons, 388 searchResultsNeeded: !Drupal.ajax_facets.ajax_facets_buttons 389 }, $this); 390 event.preventDefault(); 391 }; 392 393 /** 394 * Callback for slide event for widget ranges. 395 */ 396 Drupal.ajax_facets.processSlider = function($sliderWrapper, min, max) { 397 var $this = $(this), 398 facetName = $sliderWrapper.data('facet-name'); 399 // Init history. 400 Drupal.ajax_facets.initHistoryState($sliderWrapper); 401 // If facets are already defined in queryState. 402 if (Drupal.ajax_facets.queryState['f']) { 403 // Exclude all values for this facet from query. 404 Drupal.ajax_facets.excludeCurrentFacet(facetName); 405 Drupal.ajax_facets.queryState['f'][Drupal.ajax_facets.queryState['f'].length] = facetName + ':[' + min + ' TO ' + max + ']'; 406 } 407 408 Drupal.ajax_facets.sendAjaxQuery({ 409 pushStateNeeded: !Drupal.ajax_facets.ajax_facets_buttons, 410 searchResultsNeeded: !Drupal.ajax_facets.ajax_facets_buttons 411 }, $this); 412 }; 413 414 /** 415 * Exclude all values for this facet from query. 416 */ 417 Drupal.ajax_facets.excludeCurrentFacet = function (facetName) { 418 facetName = facetName + ':'; 419 var queryNew = []; 420 for (var index in Drupal.ajax_facets.queryState['f']) { 421 if (Drupal.ajax_facets.queryState['f'].hasOwnProperty(index)) { 422 if (Drupal.ajax_facets.queryState['f'][index].substring(0, facetName.length) != facetName) { 423 queryNew[queryNew.length] = Drupal.ajax_facets.queryState['f'][index]; 424 } 425 } 426 } 427 Drupal.ajax_facets.queryState['f'] = queryNew; 428 }; 429 430 /** 431 * Send ajax. 432 */ 433 Drupal.ajax_facets.sendAjaxQuery = function (options, $facet) { 434 Drupal.ajax_facets.beforeAjax(); 435 var data = Drupal.ajax_facets.queryState; 436 // Render the exposed filter data to send along with the aja
436x request 437 $.each(Drupal.ajax_facets.queryState.views, function (key, view) { 438 var exposedFormId = '#views-exposed-form-' + view.view_name + '-' + view.view_display_id; 439 exposedFormId = exposedFormId.replace(/\_/g, '-'); 440 $.each($(exposedFormId).serializeArray(), function (index, value) { 441 data[value.name] = value.value; 442 }); 443 }); 444 445 // Set or reset current facet. 446 Drupal.ajax_facets.current_facet_name = $facet ? $facet.data('raw-facet-name') : undefined; 447 448 // Do not auto update facets and counts if auto update is turned off in block settings. 449 if (!Drupal.settings.ajax_facets.auto_update_facets && !options.searchResultsNeeded && !options.pushStateNeeded) { 450 return; 451 } 452 453 // Notify Drupal do we need search results or not. 454 data.searchResultsNeeded = options.searchResultsNeeded; 455 456 var settings = { 457 url: encodeURI(Drupal.settings.basePath + Drupal.settings.pathPrefix + 'ajax/ajax_facets/refresh'), 458 submit: {'ajax_facets': data} 459 }; 460 var ajax = new Drupal.ajax(false, false, settings); 461 ajax.success = function (response, status) { 462 // Push new state only on successful ajax response. 463 if (options.pushStateNeeded) { 464 Drupal.ajax_facets.writeState(false); 465 } 466 // Pass back to original method. 467 Drupal.ajax.prototype.success.call(this, response, status); 468 }; 469 ajax.eventResponse(ajax, {}); 470 }; 471 472 Drupal.ajax_facets.getFacetValues = function () { 473 var f = Drupal.ajax_facets.queryState.f; 474 var facets_values = {}; 475 var symbol = ':'; 476 jQuery.each(f, function (k, v) { 477 var parts = v.split(symbol); 478 var value = parts[parts.length - 1]; 479 var appendix = symbol + value; 480 var key = decodeURIComponent(v.substr(0, v.length - appendix.length)); 481 facets_values[key] = value; 482 }); 483 return facets_values; 484 }; 485 486 /* Get view dom id for both modes of views - ajax/not ajax. */ 487 Drupal.ajax_facets.getViewDomId = function(settings, key) { 488 // We use facetapi_view_display_id to find data in settings settings.facetapi.view_dom_id, 489 // because it can has there values like view_dom_id + numeric suffix 490 // if the same views display used several times on the page. 491 var nameDisplay = settings.facetapi.views[key].view_name + ':' + settings.facetapi.views[key].facetapi_view_display_id; 492 if (settings.facetapi.view_dom_id[nameDisplay]) { 493 var view = $('.view-dom-id-' + settings.facetapi.view_dom_id[nameDisplay]); 494 if (view.length > 0) { 495 return settings.facetapi.view_dom_id[nameDisplay] 496 } 497 } 498 return false; 499 }; 500 501 /** 502 * Returns query string with selected facets. 503 */ 504 Drupal.ajax_facets.getFacetsQueryUrl = function (baseUrl) { 505 var query = { 506 'f': [] 507 }; 508 509 // Clone variable. 510 $.extend(true, query.f, Drupal.ajax_facets.queryState.f); 511 512 // Facetapi module has a bug when facet name encodes twice. 513 // For example to get this facet work 'category:name:pineapple' it should be 'category%253Aname%3Apineapple'. 514 // It means that first ':' was encoded twice. Why we don't patch facetapi? Because a lot of sites have already 515 // used facetapi module and they links (with wrong encoded facets names) has been indexed by search engines 516 // like Bing or Google. So we just bring this behaviour to ajax_facets module. 517 // Encode each facet filter name (it have already encoded once in FacetapiAjaxWidgetCheckboxes::buildListItems()). 518 for (var filter in query.f) { 519 query.f[filter] = encodeURI(query.f[filter]); 520 } 521 522 if (Drupal.ajax_facets.queryState.query) { 523 query.query = Drupal.ajax_facets.queryState.query; 524 } 525 526 if (Drupal.ajax_facets.queryState.order) { 527 query.order = Drupal.ajax_facets.queryState.order; 528 query.sort = Drupal.ajax_facets.queryState.sort; 529 } 530 531 if (Drupal.ajax_facets.queryState.pages) { 532 query.pages = Drupal.ajax_facets.queryState.pages; 533 } 534 535 // Respect existing query parameters in url. 536 // Merge respected parameters with facet parameters recursively without duplicates. 537 $.extend(true, query, Drupal.ajax_facets.simplifyObject(Drupal.ajax_facets.getAdditionalQueryParameters())); 538 539 // Add query string to base url. 540 if (!$.isEmptyObject(query)) { 541 // Clear empty items. 542 for (var item in query) { 543 if (typeof query[item] == 'undefined' || query[item].length == 0) { 544 delete query[item]; 545 } 546 } 547 var params = $.param(query); 548 // If we have params. 549 if (params.length) { 550 // This change allows selected facets with ampersands in their data to be correctly decoded - Modifed by James 2021-10-18 551 // Original only breaks when directly going to an URL in the browser e.g. /search?f[]=search_api_aggregation_2:Accounting,%20Analysis%20&%20Reporting 552 // It treats the '&' as part of the query string 'Accounting, Analysis & Reporting' 553 params = params.replace('f%5B%5D', 'f[]'); // Manually decode facet keys so we can just deal with facet values 554 let newParams = []; 555 let searchParams = new URLSearchParams(params); // Parse query -- https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams 556 for(const [key, value] of searchParams){ 557 let decodedValue = decodeURIComponent(decodeURIComponent(value)); // Double decode to make every reserved character equal (some will be double encoded, some will be single encoded) 558 newParams.push(key + '=' + encodeURIComponent(decodedValue)); // Encode component once 559 } 560 // Re-create query string 561 if(newParams){ 562 newParams = newParams.join('&'); 563 } 564 // Update params if different 565 if(params != newParams){ 566 params = newParams; 567 } 568 baseUrl += '?' + params; // No longer need to decode here as reserved characters are now properly encoded once 569 } 570 } 571 572 return baseUrl; 573 }; 574 575 /** 576 * Returns additional (not facet) query parameters from current url. 577 */ 578 Drupal.ajax_facets.getAdditionalQueryParameters = function () { 579 var result = {}; 580 581 // If we have GET params. 582 if (window.location.href.indexOf('?') !== -1) { 583 var respectedParameters = window.location.href.split('?')[1].split('&'); 584 585 // Get all query parameters in array.
586 for (var i = 0; i < respectedParameters.length; i++) { 587 var pair = respectedParameters[i].split('='); 588 589 // Remove brackets from multiple parameter. 590 pair[0] = pair[0].replace(/\[\d*\]/, ''); 591 592 // Remove encoded brackets from multiple parameter. (Added by James 2021-10-18) 593 // This fixes duplicated facets that are present in initial URL when facets are changed after load by AJAX 594 pair[0] = pair[0].replace('f%5B%5D', 'f'); 595 596 // We interested only in additional parameters but not in facet parameters. 597 if (pair[0] !== 'f') { 598 if (!result[pair[0]]) { 599 result[pair[0]] = []; 600 result[pair[0]].push(pair[1]); 601 } else { 602 result[pair[0]].push(pair[1]); 603 } 604 } 605 } 606 } 607 return result; 608 }; 609 610 /** 611 * Returns simplified object. 612 * 613 * If object has array property with only one element then that array will be turn into simple value instead of array. 614 */ 615 Drupal.ajax_facets.simplifyObject = function (obj) { 616 for (var name in obj) { 617 if (obj[name].length === 1) { 618 obj[name] = obj[name][0]; 619 } 620 } 621 622 return obj; 623 }; 624 625 /** 626 * Initialize the history state. We only want to do this on the initial page 627 * load but we can't call it until after a facet has been clicked because we 628 * need to communicate which one is being "deactivated" for our ajax success 629 * handler. 630 */ 631 Drupal.ajax_facets.initHistoryState = function ($facet) { 632 // Set the initial state only initial page load. 633 if (Drupal.ajax_facets.firstLoad) { 634 Drupal.ajax_facets.firstLoad = false; 635 Drupal.ajax_facets.writeState(true); 636 } 637 }; 638 639 /** 640 * Writes new state to browser history, either as a push or replace call. 641 * 642 * History.js library fires "statechange" event even on API push/replace calls. 643 * So before pushing new state to history we should unbind from this event and after bind again. 644 * 645 * @param {boolean} replace Set to true to replace state, rather than push it. 646 */ 647 Drupal.ajax_facets.writeState = function (replace) { 648 var stateUrl = null, 649 state = { 650 facets: Drupal.ajax_facets.queryState['f'] 651 }, 652 title = document.title, 653 method = 'replaceState'; 654 655 if (replace !== true) { 656 stateUrl = Drupal.ajax_facets.getFacetsQueryUrl(Drupal.settings.facetapi.searchUrl); 657 method = 'pushState'; 658 } 659 660 // Try to use HTML5 history object. 661 if (history[method]) { 662 history[method](state, title, stateUrl); 663 } 664 else { 665 // If history.js available - use it. 666 if (Drupal.settings.facetapi.isHistoryJsExists) { 667 var $window = $(window); 668 669 $window.unbind('statechange', Drupal.ajax_facets.reactOnStateChange); 670 History[method](state, title, stateUrl); 671 $window.bind('statechange', Drupal.ajax_facets.reactOnStateChange); 672 } 673 } 674 }; 675 676 /** 677 * Callback for back/forward browser buttons. 678 */ 679 Drupal.ajax_facets.reactOnStateChange = function () { 680 var state = null, 681 facets = []; 682 683 // Try to use HTML5 history object. 684 if (history.pushState) { 685 if (state = history.state) { 686 facets = state.facets; 687 } 688 } 689 else { 690 // If history.js available - use it. 691 if (Drupal.settings.facetapi.isHistoryJsExists) { 692 if (state = History.getState()) { 693 facets = state.data.facets; 694 } 695 } 696 } 697 698 // Do something only if paths are match and facets is defined. 699 if (window.location.pathname === Drupal.settings.facetapi.searchUrl && facets) { 700 Drupal.ajax_facets.queryState['f'] = facets; 701 Drupal.ajax_facets.force_update_results = true; 702 Drupal.ajax_facets.sendAjaxQuery({ 703 pushStateNeeded: false, 704 searchResultsNeeded: true 705 }); 706 } 707 }; 708 709 // If user opened new page and then clicked browser's back button then would not be fired "statechange" event. 710 // So we need to bind on 'statechange' event and react only once. 711 // All farther work does Drupal.ajax_facets.pushState() function. 712 // Try to use HTML5 history object. 713 if (history) { 714 window.onpopstate = function () { 715 Drupal.ajax_facets.reactOnStateChange(); 716 }; 717 } else { 718 // If history.js Adapter available - use it to bind "statechange" event. 719 if (History.Adapter && Drupal.ajax_facets.firstLoad) { 720 History.Adapter.bind(window, 'statechange', Drupal.ajax_facets.reactOnStateChange); 721 } 722 } 723 724 /* Show tooltip if facet results are not updated by ajax (in settings). */ 725 Drupal.ajax_facets.showTooltip = function ($, response) { 726 // Reset the timeout handler to avoid troubles when user is clicking on items very fast. 727 window.clearTimeout(Drupal.ajax_facets.tooltipTimeout); 728 729 // If we have chosen facet. 730 if (Drupal.ajax_facets.current_facet_name) { 731 var pos = $('[data-raw-facet-name=' + Drupal.ajax_facets.current_facet_name + ']').offset(), 732 $tooltip = $('#ajax-facets-tooltip'); 733 $tooltip.css('top', pos.top - 15); 734 $tooltip.css('left', pos.left - $tooltip.width() - 40); 735 $tooltip.show(); 736 $tooltip.find('span').html(Drupal.t('Found:') + ' ' + response.total_results); 737 738 Drupal.ajax_facets.tooltipTimeout = setTimeout(function () { 739 $tooltip.hide(250); 740 }, 3000); 741 } 742 }; 743 744 if (Drupal.ajax) { 745 // Command for process search results and facets by ajax. 746 Drupal.ajax.prototype.commands.ajax_facets_update_content = function(ajax, response) { 747 if (response.data.activeItems) { 748 Drupal.ajax_facets.facetQueryState = response.data.activeItems; 749 } 750 // After Ajax success we should update reset, apply link to handle proper redirects. 751 if (response.data.resetUrls && Drupal.settings.facetapi.facets) { 752 for (var index in Drupal.settings.facetapi.facets) { 753 if (response.data.resetUrls[Drupal.settings.facetapi.facets[index].facetName]) { 754 // Update path from response. 755 Drupal.settings.facetapi.facets[index].resetPath = response.data.resetUrls[Drupal.settings.facetapi.facets[index].facetName]; 756 } 757 } 758 } 759 760 // Update content. 761 if (response.data.newContent) { 762 for (var id in response.data.newContent) { 763 var $blockToReplace = $('#' + id + '-wrapper'); 764 if ($blockToReplace.size()) { 765 $blockToReplace.replaceWith(response.data.newContent[id]); 766 } 767 var $block = $('#' + id + '-wrapper').parents('div.block--ajax_facets:not
767(:visible)'); 768 if ($block.size()) { 769 $block.show(); 770 } 771 } 772 } 773 774 /* Update results if we have they and ajax facets buttons are not enabled or force mode is enabled. */ 775 var show_tip = false; 776 var buttons_exist = Drupal.ajax_facets.ajax_facets_buttons, 777 force_update_results = Drupal.ajax_facets.force_update_results; 778 if (!buttons_exist || force_update_results) { 779 $.each(response.data.views, function (key, view) { 780 $('.view-dom-id-' + view.view_dom_id).replaceWith(view.content); 781 }); 782 783 // Update the exposed form separately if needed. 784 $.each(response.data.views, function(key, view) { 785 if (view.exposed_form) { 786 var viewId = view.view_name + '-' + view.view_display_id; 787 $('#views-exposed-form-' + viewId.replace(/_/g, '-')).replaceWith(view.exposed_form); 788 } 789 }); 790 } 791 else { 792 show_tip = true; 793 } 794 795 // As some blocks could be empty in results of filtering - hide them. 796 if (response.data.hideBlocks) { 797 for (var id in response.data.hideBlocks) { 798 if (response.data.hideBlocks.hasOwnProperty(id)) { 799 // Search for drupal blocks (id="block-facetapi-xcogxrrjodjldoogufmypf7sc4rpofuk"). 800 var $block = $('#' + response.data.hideBlocks[id]); 801 if ($block.size()) { 802 $block.hide(); 803 } 804 // Search for panel panes (class="block-facetapi-xcogxrrjodjldoogufmypf7sc4rpofuk"). 805 else { 806 var $pane = $('.' + response.data.hideBlocks[id]); 807 if ($pane.size()) { 808 $pane.hide(); 809 } 810 } 811 } 812 } 813 } 814 815 if (response.data.settings.views) { 816 Drupal.settings.views = response.data.settings.views; 817 } 818 819 // We add this here so we can run custom code after content updates 820 Drupal.ajax_facets.afterContentUpdate(ajax, response); 821 822 Drupal.attachBehaviors(); 823 // Show the tooltip if need. 824 if (show_tip) { 825 Drupal.ajax_facets.showTooltip($, response.data); 826 } 827 }; 828 } 829})(jQuery);
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.