/**
* @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 = $(`<div>${message}</div>`);
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 <q>@field-label</q>, either due to a website problem or a network connection problem.<br>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.<string>} 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);