PageSourceSearch

https://www.tfmetalsreport.com/sites/all/modules/contrib/ajax_facets/misc/ajax_facets.js?tm5sho

js tfmetalsreport.com collected 2026-10-02 07:10:23 UTC 30,682 bytes, 802 lines download raw bytes

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