PageSourceSearch

https://legrandmix.com/core/modules/views/js/ajax_view.js?v=11.4.6

js legrandmix.com collected 2026-09-24 10:47:02 UTC 8,870 bytes, 290 lines download raw bytes

1/**
2 * @file
3 * Handles AJAX fetching of views, including filter submission and response.
4 */
5
6(function ($, Drupal, drupalSettings) {
7  /**
8   * Attaches the AJAX behavior to exposed filters forms and key View links.
9   *
10   * @type {Drupal~behavior}
11   *
12   * @prop {Drupal~behaviorAttach} attach
13   *   Attaches ajaxView functionality to relevant elements.
14   */
15  Drupal.behaviors.ViewsAjaxView = {};
16  Drupal.behaviors.ViewsAjaxView.attach = function (context, settings) {
17    if (settings?.views?.ajaxViews) {
18      const {
19        views: { ajaxViews },
20      } = settings;
21      Object.keys(ajaxViews || {}).forEach((i) => {
22        Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]);
23      });
24    }
25  };
26  Drupal.behaviors.ViewsAjaxView.detach = (context, settings, trigger) => {
27    if (trigger === 'unload') {
28      if (settings?.views?.ajaxViews) {
29        const {
30          views: { ajaxViews },
31        } = settings;
32        Object.keys(ajaxViews || {}).forEach((i) => {
33          const selector = `.js-view-dom-id-${ajaxViews[i].view_dom_id}`;
34          if ($(selector, context).length) {
35            delete Drupal.views.instances[i];
36            delete settings.views.ajaxViews[i];
37          }
38        });
39      }
40    }
41  };
42
43  /**
44   * @namespace
45   */
46  Drupal.views = {};
47
48  /**
49   * @type {object.<string, Drupal.views.ajaxView>}
50   */
51  Drupal.views.instances = {};
52
53  /**
54   * JavaScript object for a certain view.
55   *
56   * @constructor
57   *
58   * @param {object} settings
59   *   Settings object for the ajax view.
60   * @param {string} settings.view_dom_id
61   *   The DOM id of the view.
62   */
63  Drupal.views.ajaxView = function (settings) {
64    const selector = `.js-view-dom-id-${settings.view_dom_id}`;
65    this.$view = $(selector);
66    this.$exposed_form = $(
67      `form#views-exposed-form-${settings.view_name.replace(
68        /_/g,
69        '-',
70      )}-${settings.view_display_id.replace(/_/g, '-')}`,
71    );
72
73    // Retrieve the path to use for views' ajax.
74    let ajaxPath = drupalSettings.views.ajax_path;
75
76    // If there are multiple views this might've ended up showing up multiple
77    // times.
78    if (ajaxPath.constructor.toString().includes('Array')) {
79      ajaxPath = ajaxPath[0];
80    }
81
82    // Check if there are any GET parameters to send to views.
83    let queryString = window.location.search || '';
84    if (queryString !== '') {
85      // Remove the question mark and Drupal path component if any.
86      queryString = queryString.slice(1);
87
88      // Remove current exposed filters.
89      const params = decodeURI(queryString)
90        .split('&')
91        .filter((param) => {
92          const [name, value] = param.split('=');
93          return (
94            this.$exposed_form.find(`input[name="${name}"]`).length === 0 &&
95            /*
96            Submitting filters should reset paging and sorting
97            because that is what happens without AJAX.
98             */
99            !['page', 'reset', 'sort', 'order', 'q', 'render'].includes(name)
100          );
101        });
102      queryString = encodeURI(params.join('&'));
103      // If there is a '?' in ajaxPath, clean URL are on and & should be
104      // used to add parameters.
105      if (queryString !== '') {
106        queryString = (ajaxPath.includes('?') ? '&' : '?') + queryString;
107      }
108    }
109
110    this.element_settings = {
111      url: ajaxPath + queryString,
112      submit: settings,
113      httpMethod: 'GET',
114      setClick: true,
115      event: 'click',
116      selector,
117      progress: { type: 'fullscreen' },
118    };
119
120    this.settings = settings;
121
122    // Add the ajax to exposed forms.
123    once('exposed-form', this.$exposed_form).forEach(
124      this.attachExposedFormAjax.bind(this),
125    );
126
127    // Add the ajax to pagers.
128    once(
129      'ajax-pager',
130      this.$view
131        // Don't attach to nested views. Doing so would attach multiple behaviors
132        // to a given element.
133        .filter(this.filterNestedViews.bind(this)),
134    ).forEach(this.attachPagerAjax.bind(this));
135
136    // Add a trigger to update this view specifically. In order to trigger a
137    // refresh use the following code.
138    //
139    // @code
140    // $('.view-name').trigger('RefreshView');
141    // @endcode
142    const selfSettings = $.extend({}, this.element_settings, {
143      event: 'RefreshView',
144      base: this.selector,
145      httpMethod: 'GET',
146      element: this.$view.get(0),
147    });
148    this.refreshViewAjax = Drupal.ajax(selfSettings);
149  };
150
151  /**
152   * @method
153   */
154  Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () {
155    const that = this;
156    this.exposedFormAjax = [];
157    // Exclude the reset buttons so no AJAX behaviors are bound. Many things
158    // break during the form reset phase if using AJAX.
159    $(
160      'input[type=submit], button[type=submit], input[type=image]',
161      this.$exposed_form,
162    )
163      .not('[data-drupal-selector=edit-reset]')
164      .each(function (index) {
165        const selfSettings = $.extend({}, that.element_settings, {
166          base: $(this).attr('id'),
167          element: this,
168        });
169
170        selfSettings.url = that.removeExposedFiltersQueryParameters(
171          selfSettings.url,
172          that.$exposed_form,
173        );
174        that.exposedFormAjax[index] = Drupal.ajax(selfSettings);
175      });
176  };
177
178  /**
179   * Sanitizes the AJAX URL removing the parameters existing in the exposed form.
180   *
181   * @param {string} url
182   *   The AJAX URL.
183   * @param {jQuery} $exposedForm
184   *   The exposed form element.
185   *
186   * @return {string}
187   *   The modified AJAX URL without the query parameters existing in the exposed form.
188   */
189  Drupal.views.ajaxView.prototype.removeExposedFiltersQueryParameters =
190    function (url, $exposedForm) {
191      const urlObj = new URL(url, window.location.origin);
192      // Use copy to keep the iterator consistent.
193      const urlObjCopy = new URL(url, window.location.origin);
194      const cleanKeys = {};
195      let formData;
196      if ($exposedForm.is('form')) {
197          formData = new FormData($exposedForm.get(0));
198      } else {
199          // Sinon, on crée un <form> temporaire en mémoire
200          const tmpForm = document.createElement('form');
201
202          // On clone les enfants (true, true = deep + copie data/events)
203          $exposedForm.contents().clone(true, true).appendTo(tmpForm);
204          formData = new FormData(tmpForm);
205      }
206      Array.from(formData.keys()).forEach((key) => {
207        cleanKeys[key.split('[')[0]] = true;
208      });
209      urlObjCopy.searchParams.forEach((value, key) => {
210        if (cleanKeys[key.split('[')[0]]) {
211          urlObj.searchParams.delete(key);
212        }
213      });
214
215      return urlObj.pathname + urlObj.search;
216    };
217
218  /**
219   * @return {boolean}
220   *   If there is at least one parent with a view class return false.
221   */
222  Drupal.views.ajaxView.prototype.filterNestedViews = function () {
223    // If there is at least one parent with a view class, this view
224    // is nested (e.g., an attachment). Bail.
225    return !this.$view.parents('.view').length;
226  };
227
228  /**
229   * Attach the ajax behavior to each link.
230   */
231  Drupal.views.ajaxView.prototype.attachPagerAjax = function () {
232    this.$view
233      .find(
234        '.js-pager__items a, th.views-field a, .attachment .views-summary a',
235      )
236      .each(this.attachPagerLinkAjax.bind(this));
237  };
238
239  /**
240   * Attach the ajax behavior to a singe link.
241   *
242   * @param {string} [id]
243   *   The ID of the link.
244   * @param {HTMLElement} link
245   *   The link element.
246   */
247  Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) {
248    const $link = $(link);
249    const viewData = {};
250    const href = $link.attr('href');
251    // Construct an object using the settings defaults and then overriding
252    // with data specific to the link.
253    $.extend(
254      viewData,
255      this.settings,
256      Drupal.Views.parseQueryString(href),
257      // Extract argument data from the URL.
258      Drupal.Views.parseViewArgs(href, this.settings.view_base_path),
259    );
260
261    const selfSettings = $.extend({}, this.element_settings, {
262      submit: viewData,
263      base: false,
264      element: link,
265      httpMethod: 'GET',
266    });
267    selfSettings.url = this.removeExposedFiltersQueryParameters(
268      selfSettings.url,
269      this.$exposed_form,
270    );
271    this.pagerAjax = Drupal.ajax(selfSettings);
272  };
273
274  /**
275   * Sets the browser URL ajax command.
276   *
277   * @param {Drupal.Ajax} [ajax]
278   *   A {@link Drupal.ajax} object.
279   * @param {object} response
280   *   Ajax response.
281   * @param {string} response.url
282   *   URL to be set.
283   */
284  Drupal.AjaxCommands.prototype.setBrowserUrl = (ajax, response) => {
285    // Do not change browser URL if we are in a dialog wrapper.
286    if (ajax.element && !ajax.element.closest('.ui-dialog-content')) {
287      window.history.replaceState(null, '', response.url);
288    }
289  };
290})(jQuery, Drupal, drupalSettings);

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.