diff --git a/js/ajax_view.js b/js/ajax_view.js index aa10a001..8a65ea25 100644 --- a/js/ajax_view.js +++ b/js/ajax_view.js @@ -8,10 +8,68 @@ * Attaches the AJAX behavior to Views exposed filter forms and key View links. */ Drupal.behaviors.ViewsAjaxView = {}; -Drupal.behaviors.ViewsAjaxView.attach = function() { +Drupal.behaviors.ViewsAjaxView.attach = function(context, settings) { if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) { - $.each(Drupal.settings.views.ajaxViews, function(i, settings) { - Drupal.views.instances[i] = new Drupal.views.ajaxView(settings); + $.each(Drupal.settings.views.ajaxViews, function(i, viewSettings) { + Drupal.views.instances[i] = new Drupal.views.ajaxView(viewSettings); + }); + + // This attach function is invoked by two or more events: + // 1) First page load (context == document) + // 2) AJAX response (context == modified content) + // + // The views-auto-submit-processed class is applied by the $('body').once + // call in the submitExposedForm method, and should be cleared on every + // attach to prepare the environment for the user navigating back in + // history. + $('body').removeClass('views-auto-submit-processed'); + if (context == document) { + this.submitExposedForm(); + $(window).bind('popstate', jQuery.proxy(this.viewsHistoryCallback, this)); + } + } +}; + +Drupal.behaviors.ViewsAjaxView.viewsHistoryCallback = function(event) { + this.submitExposedForm(); +} + +Drupal.behaviors.ViewsAjaxView.submitExposedForm = function() { + var curFormState = history.state; + if (!$.isEmptyObject(curFormState)) { + // Was an exposed form submitted? + exposedFormSubmitted = $('form#views-exposed-form-' + curFormState.f); + + // Ensure that the form is re-submitted once + $('body').once('views-auto-submit', function() { + if (Drupal.Views.isNumeric(curFormState.p)) { + var page = $('').val(curFormState.p); + exposedFormSubmitted.prepend(page); + } + + for (var v in curFormState) { + if (curFormState.hasOwnProperty(v)) { + // Set the form element to the value from the query string + if (curFormState[v] == 'true') { + exposedFormSubmitted.find(('[id=' + v + ']')).prop('checked', true); + } + else { + if (curFormState[v] == 'false') { + exposedFormSubmitted.find(('[id=' + v + ']')).prop('checked', false); + } + else { + exposedFormSubmitted.find(('[id=' + v + ']')).prop('value', curFormState[v]); + } + } + } + } + + if (exposedFormSubmitted.hasClass('ctools-auto-submit-full-form')) { + exposedFormSubmitted.find('input, select').first().change(); + } + else { + exposedFormSubmitted.find('#' + curFormState.submit).click(); + } }); } }; @@ -59,6 +117,7 @@ Drupal.views.ajaxView = function(settings) { // Add the ajax to exposed forms. this.$exposed_form = $('#views-exposed-form-'+ settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-')); this.$exposed_form.once(jQuery.proxy(this.attachExposedFormAjax, this)); + $(document).ajaxComplete(jQuery.proxy(this.ajaxCompleteExposedCallback, this)); // Store Drupal.ajax objects here for all pager links. this.links = []; @@ -82,6 +141,70 @@ Drupal.views.ajaxView = function(settings) { this.refreshViewAjax = new Drupal.ajax(this.selector, this.$view, self_settings); }; +/** + * Store the current form state into the History queue, so that it is + * retrievable when interacting with the Browser's history. + */ +Drupal.views.ajaxView.prototype.ajaxCompleteExposedCallback = function(event, request, options) { + if (options.url === this.element_settings.url) { + // WARNING: This method will be invoked by all ajaxView instances on a page, + // and we only want to store history for the submitted exposed form of a + // single view. The submitted exposed form ID and related information is + // extracted from the AJAX request. + var requestParams = Drupal.Views.parseQueryString(options.data); + var formState = $.isEmptyObject(history.state) ? {} : history.state; + var formStateChanged = false; + var page = requestParams.hasOwnProperty('page') ? requestParams.page : 0; + var formId = requestParams.view_name.replace(/_/g, '-') + + '-' + + requestParams.view_display_id.replace(/_/g, '-'); + var exposedForm = $('form#views-exposed-form-' + formId); + var submitId = 'edit-submit-' + requestParams.view_name.replace(/_/g, '-'); + + // When a View filter is updated, multiple Ajax requests are sent, and + // we cannot be sure that the View HTML is in sync with the final result of + // the Ajax request. Since the request parameters store the form values + // submitted in the Ajax request but *not* the HTML form element IDs, and + // retrieving the Ajax request parameters is more reliable than retrieving + // the HTML form values, the Ajax request parameters will be auto-associated + // with the HTML form values. + $(exposedForm).find('select, input').each(function(i, element) { + if (!this.hasAttribute('id') || this.id == '') { + return; + } + + var requestParamId = this.id.replace(/^edit-/, '').replace(/-/g, '_'); + if (requestParams.hasOwnProperty(requestParamId)) { + if (!formState.hasOwnProperty(this.id) || formState[this.id] != requestParams[requestParamId]) { + formState[this.id] = requestParams[requestParamId]; + formStateChanged = true; + } + } + }); + + if (!formState.hasOwnProperty('p') || formState['p'] != page) { + formState['p'] = page; + formStateChanged = true; + } + + if (!formState.hasOwnProperty('f') || formState['f'] != formId) { + formState['f'] = formId; + formStateChanged = true; + } + + if (!formState.hasOwnProperty('submit') || formState['submit'] != submitId) { + formState['submit'] = submitId; + formStateChanged = true; + } + + if (formStateChanged) { + history.pushState(formState, ''); + } + + this.$exposed_form.find('input[name=page]').remove(); + } +}; + Drupal.views.ajaxView.prototype.attachExposedFormAjax = function() { var button = $('input[type=submit], button[type=submit], input[type=image]', this.$exposed_form); button = button[0]; diff --git a/js/base.js b/js/base.js index ce5bf6a8..5220b7ef 100644 --- a/js/base.js +++ b/js/base.js @@ -112,4 +112,12 @@ Drupal.Views.getPath = function (href) { return href; }; +/** + * Determines whether its argument represents a Javascript number (This is + * provided in jQuery 1.7, but we're not there yet). + */ +Drupal.Views.isNumeric = function (n) { + return !isNaN(parseFloat(n)) && isFinite(n); +}; + })(jQuery);