/** * @file * Provides utility functions for Quick Edit. */ (function($, Drupal) { /** * @namespace */ Drupal.quickedit.util = Drupal.quickedit.util || {}; /** * @namespace */ Drupal.quickedit.util.constants = {}; /** * * @type {string} */ Drupal.quickedit.util.constants.transitionEnd = 'transitionEnd.quickedit webkitTransitionEnd.quickedit transitionend.quickedit msTransitionEnd.quickedit oTransitionEnd.quickedit'; /** * Converts a field id into a formatted url path. * * @example * Drupal.quickedit.util.buildUrl( * 'node/1/body/und/full', * '/quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode' * ); * * @param {string} id * The id of an editable field. * @param {string} urlFormat * The Controller route for field processing. * * @return {string} * The formatted URL. */ Drupal.quickedit.util.buildUrl = function(id, urlFormat) { const parts = id.split('/'); return Drupal.formatString(decodeURIComponent(urlFormat), { '!entity_type': parts[0], '!id': parts[1], '!field_name': parts[2], '!langcode': parts[3], '!view_mode': parts[4], }); }; /** * Shows a network error modal dialog. * * @param {string} title * The title to use in the modal dialog. * @param {string} message * The message to use in the modal dialog. */ Drupal.quickedit.util.networkErrorModal = function(title, message) { const $message = $(`
${message}
`); const networkErrorModal = Drupal.dialog($message.get(0), { title, dialogClass: 'quickedit-network-error', buttons: [ { text: Drupal.t('OK'), click() { networkErrorModal.close(); }, primary: true, }, ], create() { $(this) .parent() .find('.ui-dialog-titlebar-close') .remove(); }, close(event) { // Automatically destroy the DOM element that was used for the dialog. $(event.target).remove(); }, }); networkErrorModal.showModal(); }; /** * @namespace */ Drupal.quickedit.util.form = { /** * Loads a form, calls a callback to insert. * * Leverages {@link Drupal.Ajax}' ability to have scoped (per-instance) * command implementations to be able to call a callback. * * @param {object} options * An object with the following keys: * @param {string} options.fieldID * The field ID that uniquely identifies the field for which this form * will be loaded. * @param {bool} options.nocssjs * Boolean indicating whether no CSS and JS should be returned (necessary * when the form is invisible to the user). * @param {bool} options.reset * Boolean indicating whether the data stored for this field's entity in * PrivateTempStore should be used or reset. * @param {function} callback * A callback function that will receive the form to be inserted, as well * as the ajax object, necessary if the callback wants to perform other * Ajax commands. */ load(options, callback) { const fieldID = options.fieldID; // Create a Drupal.ajax instance to load the form. const formLoaderAjax = Drupal.ajax({ url: Drupal.quickedit.util.buildUrl( fieldID, Drupal.url( 'quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode', ), ), submit: { nocssjs: options.nocssjs, reset: options.reset, }, error(xhr, url) { // Show a modal to inform the user of the network error. const fieldLabel = Drupal.quickedit.metadata.get(fieldID, 'label'); const message = Drupal.t( 'Could not load the form for @field-label, either due to a website problem or a network connection problem.
Please try again.', { '@field-label': fieldLabel }, ); Drupal.quickedit.util.networkErrorModal( Drupal.t('Network problem!'), message, ); // Change the state back to "candidate", to allow the user to start // in-place editing of the field again. const fieldModel = Drupal.quickedit.app.model.get('activeField'); fieldModel.set('state', 'candidate'); }, }); // Implement a scoped quickeditFieldForm AJAX command: calls the callback. formLoaderAjax.commands.quickeditFieldForm = function( ajax, response, status, ) { callback(response.data, ajax); Drupal.ajax.instances[this.instanceIndex] = null; }; // This will ensure our scoped quickeditFieldForm AJAX command gets // called. formLoaderAjax.execute(); }, /** * Creates a {@link Drupal.Ajax} instance that is used to save a form. * * @param {object} options * Submit options to the form. * @param {bool} options.nocssjs * Boolean indicating whether no CSS and JS should be returned (necessary * when the form is invisible to the user). * @param {Array.} options.other_view_modes * Array containing view mode IDs (of other instances of this field on the * page). * @param {jQuery} $submit * The submit element. * * @return {Drupal.Ajax} * A {@link Drupal.Ajax} instance. */ ajaxifySaving(options, $submit) { // Re-wire the form to handle submit. const settings = { url: $submit.closest('form').attr('action'), setClick: true, event: 'click.quickedit', progress: false, submit: { nocssjs: options.nocssjs, other_view_modes: options.other_view_modes, }, /** * Reimplement the success handler. * * Ensure {@link Drupal.attachBehaviors} does not get called on the * form. * * @param {Drupal.AjaxCommands~commandDefinition} response * The Drupal AJAX response. * @param {number} [status] * The HTTP status code. */ success(response, status) { Object.keys(response || {}).forEach(i => { if (response[i].command && this.commands[response[i].command]) { this.commands[response[i].command](this, response[i], status); } }); }, base: $submit.attr('id'), element: $submit[0], }; return Drupal.ajax(settings); }, /** * Cleans up the {@link Drupal.Ajax} instance that is used to save the form. * * @param {Drupal.Ajax} ajax * A {@link Drupal.Ajax} instance that was returned by * {@link Drupal.quickedit.form.ajaxifySaving}. */ unajaxifySaving(ajax) { $(ajax.element).off('click.quickedit'); }, }; })(jQuery, Drupal);