/** * @file * JavaScript behaviors for Ajax. */ (function ($, Drupal, drupalSettings) { 'use strict'; Drupal.webform = Drupal.webform || {}; Drupal.webform.ajax = Drupal.webform.ajax || {}; // Allow scrollTopOffset to be custom defined or based on whether there is a // floating toolbar. Drupal.webform.ajax.scrollTopOffset = Drupal.webform.ajax.scrollTopOffset || ($('#toolbar-administration').length ? 140 : 10); // Set global scroll top offset. // @todo Remove in Webform 6.x. Drupal.webform.scrollTopOffset = Drupal.webform.ajax.scrollTopOffset; /** * Provide Webform Ajax link behavior. * * Display fullscreen progress indicator instead of throbber. * Copied from: Drupal.behaviors.AJAX * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches the behavior to a.webform-ajax-link. */ Drupal.behaviors.webformAjaxLink = { attach: function (context) { $('.webform-ajax-link', context).once('webform-ajax-link').each(function () { var element_settings = {}; element_settings.progress = {type: 'fullscreen'}; // For anchor tags, these will go to the target of the anchor rather // than the usual location. var href = $(this).attr('href'); if (href) { element_settings.url = href; element_settings.event = 'click'; } element_settings.dialogType = $(this).data('dialog-type'); element_settings.dialogRenderer = $(this).data('dialog-renderer'); element_settings.dialog = $(this).data('dialog-options'); element_settings.base = $(this).attr('id'); element_settings.element = this; Drupal.ajax(element_settings); // Close all open modal dialogs when opening off-canvas dialog. if (element_settings.dialogRenderer === 'off_canvas') { $(this).on('click', function () { $('.ui-dialog.webform-ui-dialog:visible').find('.ui-dialog-content').dialog('close'); }); } }); } }; /** * Adds a hash (#) to current pages location for links and buttons * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches the behavior to a[data-hash] or :button[data-hash]. * * @see \Drupal\webform_ui\WebformUiEntityElementsForm::getElementRow * @see Drupal.behaviors.webformFormTabs */ Drupal.behaviors.webformAjaxHash = { attach: function (context) { $('[data-hash]', context).once('webform-ajax-hash').each(function () { var hash = $(this).data('hash'); if (hash) { $(this).on('click', function () { location.hash = $(this).data('hash'); }); } }); } }; /** * Provide Ajax callback for confirmation back to link. * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches the behavior to confirmation back to link. */ Drupal.behaviors.webformConfirmationBackAjax = { attach: function (context) { $('.js-webform-confirmation-back-link-ajax', context) .once('webform-confirmation-back-ajax') .on('click', function (event) { var $form = $(this).parents('form'); // Trigger the Ajax call back for the hidden submit button. // @see \Drupal\webform\WebformSubmissionForm::getCustomForm $form.find('.js-webform-confirmation-back-submit-ajax').trigger('click'); // Move the progress indicator from the submit button to after this link. // @todo Figure out a better way to set a progress indicator. var $progress_indicator = $form.find('.ajax-progress'); if ($progress_indicator) { $(this).after($progress_indicator); } // Cancel the click event. event.preventDefault(); event.stopPropagation(); }); } }; /** ********************************************************************** **/ // Ajax commands. /** ********************************************************************** **/ /** * Track the updated table row key. */ var updateKey; /** * Track the add element key. */ var addElement; /** * Command to insert new content into the DOM. * * @param {Drupal.Ajax} ajax * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. * @param {object} response * The response from the Ajax request. * @param {string} response.data * The data to use with the jQuery method. * @param {string} [response.method] * The jQuery DOM manipulation method to be used. * @param {string} [response.selector] * A optional jQuery selector string. * @param {object} [response.settings] * An optional array of settings that will be used. * @param {number} [status] * The XMLHttpRequest status. */ Drupal.AjaxCommands.prototype.webformInsert = function (ajax, response, status) { // Insert the HTML. this.insert(ajax, response, status); // Add element. if (addElement) { var addSelector = (addElement === '_root_') ? '#webform-ui-add-element' : '[data-drupal-selector="edit-webform-ui-elements-' + addElement + '-add"]'; $(addSelector).trigger('click'); } // If not add element, then scroll to and highlight the updated table row. if (!addElement && updateKey) { var $element = $('tr[data-webform-key="' + updateKey + '"]'); // Highlight the updated element's row. $element.addClass('color-success'); setTimeout(function () {$element.removeClass('color-success');}, 3000); // Focus first tabbable item for the updated elements and handlers. $element.find(':tabbable:not(.tabledrag-handle)').eq(0).trigger('focus'); // Scroll element into view. Drupal.webformScrolledIntoView($element); } else { // Focus main content. $('#main-content').trigger('focus'); } // Display main page's status message in a floating container. var $wrapper = $(response.selector); if ($wrapper.parents('.ui-dialog').length === 0) { var $messages = $wrapper.find('.messages'); // If 'add element' don't show any messages. if (addElement) { $messages.remove(); } else if ($messages.length) { var $floatingMessage = $('#webform-ajax-messages'); if ($floatingMessage.length === 0) { $floatingMessage = $('
'); $('body').append($floatingMessage); } if ($floatingMessage.is(':animated')) { $floatingMessage.stop(true, true); } $floatingMessage.html($messages).show().delay(3000).fadeOut(1000); } } updateKey = null; // Reset element update. addElement = null; // Reset add element. }; /** * Scroll to top ajax command. * * @param {Drupal.Ajax} [ajax] * A {@link Drupal.ajax} object. * @param {object} response * Ajax response. * @param {string} response.selector * Selector to use. * * @see Drupal.AjaxCommands.prototype.viewScrollTop */ Drupal.AjaxCommands.prototype.webformScrollTop = function (ajax, response) { // Scroll top. Drupal.webformScrollTop(response.selector, response.target); // Focus on the form wrapper content bookmark if // .js-webform-autofocus is not enabled. // @see \Drupal\webform\Form\WebformAjaxFormTrait::buildAjaxForm var $form = $(response.selector + '-content').find('form'); if (!$form.hasClass('js-webform-autofocus')) { $(response.selector + '-content').trigger('focus'); } }; /** * Command to refresh the current webform page. * * @param {Drupal.Ajax} [ajax] * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. * @param {object} response * The response from the Ajax request. * @param {string} response.url * The URL to redirect to. * @param {number} [status] * The XMLHttpRequest status. */ Drupal.AjaxCommands.prototype.webformRefresh = function (ajax, response, status) { // Get URL path name. // @see https://stackoverflow.com/questions/6944744/javascript-get-portion-of-url-path var a = document.createElement('a'); a.href = response.url; var forceReload = (response.url.match(/\?reload=([^&]+)($|&)/)) ? RegExp.$1 : null; if (forceReload) { response.url = response.url.replace(/\?reload=([^&]+)($|&)/, ''); this.redirect(ajax, response, status); return; } if (a.pathname === window.location.pathname && $('.webform-ajax-refresh').length) { updateKey = (response.url.match(/[?|&]update=([^&]+)($|&)/)) ? RegExp.$1 : null; addElement = (response.url.match(/[?|&]add_element=([^&]+)($|&)/)) ? RegExp.$1 : null; $('.webform-ajax-refresh').trigger('click'); } else { // Clear unsaved information flag so that the current webform page // can be redirected. // @see Drupal.behaviors.webformUnsaved.clear if (Drupal.behaviors.webformUnsaved) { Drupal.behaviors.webformUnsaved.clear(); } // For webform embedded in an iframe, open all redirects in the top // of the browser window. // @see \Drupal\webform_share\Controller\WebformShareController::page if (drupalSettings.webform_share && drupalSettings.webform_share.page) { window.top.location = response.url; } else { this.redirect(ajax, response, status); } } }; /** * Command to close a off-canvas and modal dialog. * * If no selector is given, it defaults to trying to close the modal. * * @param {Drupal.Ajax} [ajax] * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. * @param {object} response * The response from the Ajax request. * @param {string} response.selector * Selector to use. * @param {bool} response.persist * Whether to persist the dialog element or not. * @param {number} [status] * The HTTP status code. */ Drupal.AjaxCommands.prototype.webformCloseDialog = function (ajax, response, status) { if ($('#drupal-off-canvas').length) { // Close off-canvas system tray which is not triggered by close dialog // command. // @see Drupal.behaviors.offCanvasEvents $('#drupal-off-canvas').remove(); $('body').removeClass('js-tray-open'); // Remove all *.off-canvas events $(document).off('.off-canvas'); $(window).off('.off-canvas'); var edge = document.documentElement.dir === 'rtl' ? 'left' : 'right'; var $mainCanvasWrapper = $('[data-off-canvas-main-canvas]'); $mainCanvasWrapper.css('padding-' + edge, 0); // Resize tabs when closing off-canvas system tray. $(window).trigger('resize.tabs'); } // https://stackoverflow.com/questions/15763909/jquery-ui-dialog-check-if-exists-by-instance-method if ($(response.selector).hasClass('ui-dialog-content')) { this.closeDialog(ajax, response, status); } }; /** * Triggers confirm page reload. * * @param {Drupal.Ajax} [ajax] * A {@link Drupal.ajax} object. * @param {object} response * Ajax response. * @param {string} response.message * A message to be displayed in the confirm dialog. */ Drupal.AjaxCommands.prototype.webformConfirmReload = function (ajax, response) { if (window.confirm(response.message)) { window.location.reload(true); } }; })(jQuery, Drupal, drupalSettings);