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);