ajax_view.es6.js 6.4 KB

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