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