1/** 2 * @file 3 * JavaScript behaviors for Ajax. 4 */ 5 6(function ($, Drupal, drupalSettings, once, tabbable) { 7 Drupal.webform = Drupal.webform || {}; 8 Drupal.webform.ajax = Drupal.webform.ajax || {}; 9 // Allow scrollTopOffset to be custom defined or based on whether there is a 10 // floating toolbar. 11 Drupal.webform.ajax.scrollTopOffset = Drupal.webform.ajax.scrollTopOffset || ($('#toolbar-administration').length ? 140 : 10); 12 13 // Set global scroll top offset. 14 // @todo Remove in Webform 6.x. 15 Drupal.webform.scrollTopOffset = Drupal.webform.ajax.scrollTopOffset; 16 17 /** 18 * Provide Webform Ajax link behavior. 19 * 20 * Display fullscreen progress indicator instead of throbber. 21 * Copied from: Drupal.behaviors.AJAX 22 * 23 * @type {Drupal~behavior} 24 * 25 * @prop {Drupal~behaviorAttach} attach 26 * Attaches the behavior to a.webform-ajax-link. 27 */ 28 Drupal.behaviors.webformAjaxLink = { 29 attach(context) { 30 $(once('webform-ajax-link', '.webform-ajax-link', context)).each(function () { 31 var element_settings = {}; 32 element_settings.progress = {type: 'fullscreen'}; 33 34 // For anchor tags, these will go to the target of the anchor rather 35 // than the usual location. 36 var href = $(this).attr('href'); 37 if (href) { 38 element_settings.url = href; 39 element_settings.event = 'click'; 40 } 41 element_settings.dialogType = $(this).data('dialog-type'); 42 element_settings.dialogRenderer = $(this).data('dialog-renderer'); 43 element_settings.dialog = $(this).data('dialog-options'); 44 element_settings.base = $(this).attr('id'); 45 element_settings.element = this; 46 Drupal.ajax(element_settings); 47 48 // Close all open modal dialogs when opening off-canvas dialog. 49 if (element_settings.dialogRenderer === 'off_canvas') { 50 $(this).on('click', function () { 51 $('.ui-dialog.webform-ui-dialog:visible').find('.ui-dialog-content').dialog('close'); 52 }); 53 } 54 }); 55 } 56 }; 57 58 /** 59 * Adds a hash (#) to current pages location for links and buttons 60 * 61 * @type {Drupal~behavior} 62 * 63 * @prop {Drupal~behaviorAttach} attach 64 * Attaches the behavior to a[data-hash] or :button[data-hash]. 65 * 66 * @see \Drupal\webform_ui\WebformUiEntityElementsForm::getElementRow 67 * @see Drupal.behaviors.webformFormTabs 68 */ 69 Drupal.behaviors.webformAjaxHash = { 70 attach(context) { 71 $(once('webform-ajax-hash', '[data-hash]', context)).each(function () { 72 var hash = $(this).data('hash'); 73 if (hash) { 74 $(this).on('click', function () { 75 location.hash = $(this).data('hash'); 76 }); 77 } 78 }); 79 } 80 }; 81 82 /** 83 * Provide Ajax callback for confirmation back to link. 84 * 85 * @type {Drupal~behavior} 86 * 87 * @prop {Drupal~behaviorAttach} attach 88 * Attaches the behavior to confirmation back to link. 89 */ 90 Drupal.behaviors.webformConfirmationBackAjax = { 91 attach(context) { 92 $(once('webform-confirmation-back-ajax', '.js-webform-confirmation-back-link-ajax', context)) 93 .on('click', function (event) { 94 var $form = $(this).parents('form'); 95 96 // Trigger the Ajax call back for the hidden submit button. 97 // @see \Drupal\webform\WebformSubmissionForm::getCustomForm 98 $form.find('.js-webform-confirmation-back-submit-ajax').trigger('click'); 99 100 // Move the progress indicator from the submit button to after this link. 101 // @todo Figure out a better way to set a progress indicator. 102 var $progress_indicator = $form.find('.ajax-progress'); 103 if ($progress_indicator) { 104 $(this).after($progress_indicator); 105 } 106 107 // Cancel the click event. 108 event.preventDefault(); 109 event.stopPropagation(); 110 }); 111 } 112 }; 113 114 /** ********************************************************************** **/ 115 // Ajax commands. 116 /** ********************************************************************** **/ 117 118 /** 119 * Track the updated table row key. 120 */ 121 var updateKey; 122 123 /** 124 * Track the add element key. 125 */ 126 var addElement; 127 128 /** 129 * Command to insert new content into the DOM. 130 * 131 * @param {Drupal.Ajax} ajax 132 * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. 133 * @param {object} response 134 * The response from the Ajax request. 135 * @param {string} response.data 136 * The data to use with the jQuery method. 137 * @param {string} [response.method] 138 * The jQuery DOM manipulation method to be used. 139 * @param {string} [response.selector] 140 * A optional jQuery selector string. 141 * @param {object} [response.settings] 142 * An optional array of settings that will be used. 143 * @param {number} [status] 144 * The XMLHttpRequest status. 145 */ 146 Drupal.AjaxCommands.prototype.webformInsert = function (ajax, response, status) { 147 // Insert the HTML. 148 this.insert(ajax, response, status); 149 150 // Add element. 151 if (addElement) { 152 var addSelector = (addElement === '_root_') 153 ? '#webform-ui-add-element' 154 : '[data-drupal-selector="edit-webform-ui-elements-' + addElement + '-add"]'; 155 $(addSelector).trigger('click'); 156 } 157 158 // If not add element, then scroll to and highlight the updated table row. 159 if (!addElement && updateKey) { 160 var $element = $('tr[data-webform-key="' + updateKey + '"]'); 161 162 // Highlight the updated element's row. 163 $element.addClass('color-success'); 164 setTimeout(function () {$element.removeClass('color-success');}, 3000); 165 166 // Focus first tabbable item for the updated elements and handlers. 167 const tabbableElements = tabbable.tabbable($element.get(0)); 168 const filteredElements = tabbableElements.filter(element => !element.classList.contains('tabledrag-handle')); 169 if (filteredElements.length) { 170 filteredElements[0].focus(); 171 } 172 173 // Scroll element into view. 174 Drupal.webformScrolledIntoView($element); 175 } 176 else { 177 // Focus main content. 178 $('#main-content').trigger('focus'); 179 } 180 181 // Display main page's status message in a floating container. 182 var $wrapper = $(response.selector); 183 if ($wrapper.parents('.ui-dialog').length === 0) { 184 var $messages = $wrapper.find('.messages'); 185 // If 'add element' don't show any messages. 186 if (addElement) { 187 $messages.remove(); 188 } 189 else if ($messages.length) { 190 var $floatingMessage = $('#webform-ajax-messages'); 191 if ($floatingMessage.length === 0) { 192 $floatingMessage = $('<div id="webform-ajax-messages" class="webform-ajax-messages"></div>'); 193 $('body').append($floatingMessage); 194 } 195 if ($floatingMessage.is(':animated')) { 196 $floatingMessage.stop(true, true); 197 } 198 $floatingMessage.html($messages).show().delay(3000).fadeOut(1000); 199 } 200 } 201 202 updateKey = null; // Reset element update. 203 addElement = null; // Reset add element. 204 }; 205 206 /** 207 * Scroll to top ajax command. 208 * 209 * @param {Drupal.Ajax} [ajax] 210 * A {@link Drupal.ajax} object. 211 * @param {object} response 212 * Ajax response. 213 * @param {string} response.selector 214 * Selector to use. 215 * 216 * @see Drupal.AjaxCommands.prototype.viewScrollTop 217 */ 218 Drupal.AjaxCommands.prototype.webformScrollTop = function (ajax, response) { 219 // Scroll top. 220 Drupal.webformScrollTop(response.selector, response.target); 221 222 // Focus on the form wrapper content bookmark if 223 // .js-webform-autofocus is not enabled. 224 // @see \Drupal\webform\Form\WebformAjaxFormTrait::buildAjaxForm 225 var $form = $(response.selector + '-content').find('form'); 226 if (!$form.hasClass('js-webform-autofocus')) { 227 $(response.selector + '-content').trigger('focus'); 228 } 229 }; 230 231 /** 232 * Command to refresh the current webform page. 233 * 234 * @param {Drupal.Ajax} [ajax] 235 * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. 236 * @param {object} response 237 * The response from the Ajax request. 238 * @param {string} response.url 239 * The URL to redirect to. 240 * @param {number} [status] 241 * The XMLHttpRequest status. 242 */ 243 Drupal.AjaxCommands.prototype.webformRefresh = function (ajax, response, status) { 244 // Get URL path name. 245 // @see https://stackoverflow.com/questions/6944744/javascript-get-portion-of-url-path 246 var a = document.createElement('a'); 247 a.href = response.url; 248 var forceReload = (response.url.match(/\?reload=([^&]+)($|&)/)) ? RegExp.$1 : null; 249 if (forceReload) { 250 response.url = response.url.replace(/\?reload=([^&]+)($|&)/, ''); 251 this.redirect(ajax, response, status); 252 return; 253 } 254 255 if (a.pathname === window.location.pathname && $('.webform-ajax-refresh').length) { 256 updateKey = (response.url.match(/[?|&]update=([^&]+)($|&)/)) ? RegExp.$1 : null; 257 addElement = (response.url.match(/[?|&]add_element=([^&]+)($|&)/)) ? RegExp.$1 : null; 258 $('.webform-ajax-refresh').trigger('click'); 259 // Ensure the off canvas dialog is always closed and removed. 260 // @see Drupal.AjaxCommands.prototype.closeDialog 261 $('#drupal-off-canvas').hide(); 262 } 263 else { 264 // Clear unsaved information flag so that the current webform page 265 // can be redirected. 266 // @see Drupal.behaviors.webformUnsaved.clear 267 if (Drupal.behaviors.webformUnsaved) { 268 Drupal.behaviors.webformUnsaved.clear(); 269 } 270 271 // For webform embedded in an iframe, open all redirects in the top 272 // of the browser window. 273 // @see \Drupal\webform_share\Controller\WebformShareController::page 274 if (drupalSettings.webform_share && 275 drupalSettings.webform_share.page) { 276 window.top.location = response.url; 277 } 278 else { 279 this.redirect(ajax, response, status); 280 } 281 } 282 }; 283 284 /** 285 * Command to close a off-canvas and modal dialog. 286 * 287 * If no selector is given, it defaults to trying to close the modal. 288 * 289 * @param {Drupal.Ajax} [ajax] 290 * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. 291 * @param {object} response 292 * The response from the Ajax request. 293 * @param {string} response.selector 294 * Selector to use. 295 * @param {bool} response.persist 296 * Whether to persist the dialog element or not. 297 * @param {number} [status] 298 * The HTTP status code. 299 */ 300 Drupal.AjaxCommands.prototype.webformCloseDialog = function (ajax, response, status) { 301 if ($('#drupal-off-canvas-wrapper').length) { 302 // Close off-canvas system tray which is not triggered by close dialog 303 // command. 304 // @see Drupal.behaviors.offCanvasEvents 305 $('#drupal-off-canvas-wrapper').remove(); 306 $('body').removeClass('js-tray-open'); 307 // Remove all *.off-canvas events 308 $(document).off('.off-canvas'); 309 $(window).off('.off-canvas'); 310 var edge = document.documentElement.dir === 'rtl' ? 'left' : 'right'; 311 var $mainCanvasWrapper = $('[data-off-canvas-main-canvas]'); 312 $mainCanvasWrapper.css('padding-' + edge, 0); 313 314 // Resize tabs when closing off-canvas system tray. 315 $(window).trigger('resize.tabs'); 316 } 317 318 // https://stackoverflow.com/questions/15763909/jquery-ui-dialog-check-if-exists-by-instance-method 319 if ($(response.selector).hasClass('ui-dialog-content')) { 320 this.closeDialog(ajax, response, status); 321 } 322 }; 323 324 /** 325 * Triggers confirm page reload. 326 * 327 * @param {Drupal.Ajax} [ajax] 328 * A {@link Drupal.ajax} object. 329 * @param {object} response 330 * Ajax response. 331 * @param {string} response.message 332 * A message to be displayed in the confirm dialog. 333 */ 334 Drupal.AjaxCommands.prototype.webformConfirmReload = function (ajax, response) { 335 if (window.confirm(response.message)) { 336 window.location.reload(true); 337 } 338 }; 339 340})(jQuery, Drupal, drupalSettings, once, tabbable);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.