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