0c8535890b5a005ae7f1f3da748ccc0e6b038d65
[yaffs-website] / web / core / modules / views / js / ajax_view.es6.js
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 () {
17     if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) {
18       const ajaxViews = drupalSettings.views.ajaxViews;
19       Object.keys(ajaxViews || {}).forEach((i) => {
20         Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]);
21       });
22     }
23   };
24
25   /**
26    * @namespace
27    */
28   Drupal.views = {};
29
30   /**
31    * @type {object.<string, Drupal.views.ajaxView>}
32    */
33   Drupal.views.instances = {};
34
35   /**
36    * Javascript object for a certain view.
37    *
38    * @constructor
39    *
40    * @param {object} settings
41    *   Settings object for the ajax view.
42    * @param {string} settings.view_dom_id
43    *   The DOM id of the view.
44    */
45   Drupal.views.ajaxView = function (settings) {
46     const selector = `.js-view-dom-id-${settings.view_dom_id}`;
47     this.$view = $(selector);
48
49     // Retrieve the path to use for views' ajax.
50     let ajaxPath = drupalSettings.views.ajax_path;
51
52     // If there are multiple views this might've ended up showing up multiple
53     // times.
54     if (ajaxPath.constructor.toString().indexOf('Array') !== -1) {
55       ajaxPath = ajaxPath[0];
56     }
57
58     // Check if there are any GET parameters to send to views.
59     let queryString = window.location.search || '';
60     if (queryString !== '') {
61       // Remove the question mark and Drupal path component if any.
62       queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, '');
63       if (queryString !== '') {
64         // If there is a '?' in ajaxPath, clean url are on and & should be
65         // used to add parameters.
66         queryString = ((/\?/.test(ajaxPath)) ? '&' : '?') + queryString;
67       }
68     }
69
70     this.element_settings = {
71       url: ajaxPath + queryString,
72       submit: settings,
73       setClick: true,
74       event: 'click',
75       selector,
76       progress: { type: 'fullscreen' },
77     };
78
79     this.settings = settings;
80
81     // Add the ajax to exposed forms.
82     this.$exposed_form = $(`form#views-exposed-form-${settings.view_name.replace(/_/g, '-')}-${settings.view_display_id.replace(/_/g, '-')}`);
83     this.$exposed_form.once('exposed-form').each($.proxy(this.attachExposedFormAjax, this));
84
85     // Add the ajax to pagers.
86     this.$view
87       // Don't attach to nested views. Doing so would attach multiple behaviors
88       // to a given element.
89       .filter($.proxy(this.filterNestedViews, this))
90       .once('ajax-pager').each($.proxy(this.attachPagerAjax, this));
91
92     // Add a trigger to update this view specifically. In order to trigger a
93     // refresh use the following code.
94     //
95     // @code
96     // $('.view-name').trigger('RefreshView');
97     // @endcode
98     const selfSettings = $.extend({}, this.element_settings, {
99       event: 'RefreshView',
100       base: this.selector,
101       element: this.$view.get(0),
102     });
103     this.refreshViewAjax = Drupal.ajax(selfSettings);
104   };
105
106   /**
107    * @method
108    */
109   Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () {
110     const that = this;
111     this.exposedFormAjax = [];
112     // Exclude the reset buttons so no AJAX behaviours are bound. Many things
113     // break during the form reset phase if using AJAX.
114     $('input[type=submit], input[type=image]', this.$exposed_form).not('[data-drupal-selector=edit-reset]').each(function (index) {
115       const selfSettings = $.extend({}, that.element_settings, {
116         base: $(this).attr('id'),
117         element: this,
118       });
119       that.exposedFormAjax[index] = Drupal.ajax(selfSettings);
120     });
121   };
122
123   /**
124    * @return {bool}
125    *   If there is at least one parent with a view class return false.
126    */
127   Drupal.views.ajaxView.prototype.filterNestedViews = function () {
128     // If there is at least one parent with a view class, this view
129     // is nested (e.g., an attachment). Bail.
130     return !this.$view.parents('.view').length;
131   };
132
133   /**
134    * Attach the ajax behavior to each link.
135    */
136   Drupal.views.ajaxView.prototype.attachPagerAjax = function () {
137     this.$view.find('ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a')
138       .each($.proxy(this.attachPagerLinkAjax, this));
139   };
140
141   /**
142    * Attach the ajax behavior to a singe link.
143    *
144    * @param {string} [id]
145    *   The ID of the link.
146    * @param {HTMLElement} link
147    *   The link element.
148    */
149   Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) {
150     const $link = $(link);
151     const viewData = {};
152     const href = $link.attr('href');
153     // Construct an object using the settings defaults and then overriding
154     // with data specific to the link.
155     $.extend(
156       viewData,
157       this.settings,
158       Drupal.Views.parseQueryString(href),
159       // Extract argument data from the URL.
160       Drupal.Views.parseViewArgs(href, this.settings.view_base_path),
161     );
162
163     const selfSettings = $.extend({}, this.element_settings, {
164       submit: viewData,
165       base: false,
166       element: link,
167     });
168     this.pagerAjax = Drupal.ajax(selfSettings);
169   };
170
171   /**
172    * Views scroll to top ajax command.
173    *
174    * @param {Drupal.Ajax} [ajax]
175    *   A {@link Drupal.ajax} object.
176    * @param {object} response
177    *   Ajax response.
178    * @param {string} response.selector
179    *   Selector to use.
180    */
181   Drupal.AjaxCommands.prototype.viewsScrollTop = function (ajax, response) {
182     // Scroll to the top of the view. This will allow users
183     // to browse newly loaded content after e.g. clicking a pager
184     // link.
185     const offset = $(response.selector).offset();
186     // We can't guarantee that the scrollable object should be
187     // the body, as the view could be embedded in something
188     // more complex such as a modal popup. Recurse up the DOM
189     // and scroll the first element that has a non-zero top.
190     let scrollTarget = response.selector;
191     while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) {
192       scrollTarget = $(scrollTarget).parent();
193     }
194     // Only scroll upward.
195     if (offset.top - 10 < $(scrollTarget).scrollTop()) {
196       $(scrollTarget).animate({ scrollTop: (offset.top - 10) }, 500);
197     }
198   };
199 }(jQuery, Drupal, drupalSettings));