Initial Drupal 11 with DDEV setup
This commit is contained in:
34
web/core/modules/editor/js/editor.dialog.js
Normal file
34
web/core/modules/editor/js/editor.dialog.js
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @file
|
||||
* AJAX commands used by Editor module.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
/**
|
||||
* Command to save the contents of an editor-provided modal.
|
||||
*
|
||||
* This command does not close the open modal. It should be followed by a
|
||||
* call to `Drupal.AjaxCommands.prototype.closeDialog`. Editors that are
|
||||
* integrated with dialogs must independently listen for an
|
||||
* `editor:dialogsave` event to save the changes into the contents of their
|
||||
* interface.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The Drupal.Ajax object.
|
||||
* @param {object} response
|
||||
* The server response from the ajax request.
|
||||
* @param {Array} response.values
|
||||
* The values that were saved.
|
||||
* @param {number} [status]
|
||||
* The status code from the ajax request.
|
||||
*
|
||||
* @fires event:editor:dialogsave
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.editorDialogSave = function (
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
$(window).trigger('editor:dialogsave', [response.values]);
|
||||
};
|
||||
})(jQuery, Drupal);
|
||||
339
web/core/modules/editor/js/editor.js
Normal file
339
web/core/modules/editor/js/editor.js
Normal file
@ -0,0 +1,339 @@
|
||||
/**
|
||||
* @file
|
||||
* Attaches behavior for the Editor module.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
/**
|
||||
* Finds the text area field associated with the given text format selector.
|
||||
*
|
||||
* @param {jQuery} $formatSelector
|
||||
* A text format selector DOM element.
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
* The text area DOM element, if it was found.
|
||||
*/
|
||||
function findFieldForFormatSelector($formatSelector) {
|
||||
const fieldId = $formatSelector.attr('data-editor-for');
|
||||
// This selector will only find text areas in the top-level document. We do
|
||||
// not support attaching editors on text areas within iframes.
|
||||
return $(`#${fieldId}`).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter away XSS attack vectors when switching text formats.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} originalFormatID
|
||||
* The text format ID of the original text format.
|
||||
* @param {function} callback
|
||||
* A callback to be called (with no parameters) after the field's value has
|
||||
* been XSS filtered.
|
||||
*/
|
||||
function filterXssWhenSwitching(field, format, originalFormatID, callback) {
|
||||
// A text editor that already is XSS-safe needs no additional measures.
|
||||
if (format.editor.isXssSafe) {
|
||||
callback(field, format);
|
||||
}
|
||||
// Otherwise, ensure XSS safety: let the server XSS filter this value.
|
||||
else {
|
||||
$.ajax({
|
||||
url: Drupal.url(`editor/filter_xss/${format.format}`),
|
||||
type: 'POST',
|
||||
data: {
|
||||
value: field.value,
|
||||
original_format_id: originalFormatID,
|
||||
},
|
||||
dataType: 'json',
|
||||
success(xssFilteredValue) {
|
||||
// If the server returns false, then no XSS filtering is needed.
|
||||
if (xssFilteredValue !== false) {
|
||||
field.value = xssFilteredValue;
|
||||
}
|
||||
callback(field, format);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the text editor on a text area.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The text area DOM element.
|
||||
* @param {string} newFormatID
|
||||
* The text format we're changing to; the text editor for the currently
|
||||
* active text format will be detached, and the text editor for the new text
|
||||
* format will be attached.
|
||||
*/
|
||||
function changeTextEditor(field, newFormatID) {
|
||||
const previousFormatID = field.getAttribute(
|
||||
'data-editor-active-text-format',
|
||||
);
|
||||
|
||||
// Detach the current editor (if any) and attach a new editor.
|
||||
if (drupalSettings.editor.formats[previousFormatID]) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
drupalSettings.editor.formats[previousFormatID],
|
||||
);
|
||||
}
|
||||
// When no text editor is currently active, stop tracking changes.
|
||||
else {
|
||||
$(field).off('.editor');
|
||||
}
|
||||
|
||||
// Attach the new text editor (if any).
|
||||
if (drupalSettings.editor.formats[newFormatID]) {
|
||||
const format = drupalSettings.editor.formats[newFormatID];
|
||||
filterXssWhenSwitching(
|
||||
field,
|
||||
format,
|
||||
previousFormatID,
|
||||
Drupal.editorAttach,
|
||||
);
|
||||
}
|
||||
|
||||
// Store the new active format.
|
||||
field.setAttribute('data-editor-active-text-format', newFormatID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in text format.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* The text format change event.
|
||||
*/
|
||||
function onTextFormatChange(event) {
|
||||
const select = event.target;
|
||||
const field = event.data.field;
|
||||
const activeFormatID = field.getAttribute('data-editor-active-text-format');
|
||||
const newFormatID = select.value;
|
||||
|
||||
// Prevent double-attaching if the change event is triggered manually.
|
||||
if (newFormatID === activeFormatID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When changing to a text format that has a text editor associated
|
||||
// with it that supports content filtering, then first ask for
|
||||
// confirmation, because switching text formats might cause certain
|
||||
// markup to be stripped away.
|
||||
const supportContentFiltering =
|
||||
drupalSettings.editor.formats[newFormatID]
|
||||
?.editorSupportsContentFiltering;
|
||||
// If there is no content yet, it's always safe to change the text format.
|
||||
const hasContent = field.value !== '';
|
||||
if (hasContent && supportContentFiltering) {
|
||||
const message = Drupal.t(
|
||||
'Changing the text format to %text_format will permanently remove content that is not allowed in that text format.<br><br>Save your changes before switching the text format to avoid losing data.',
|
||||
{
|
||||
'%text_format': $(select).find('option:selected')[0].textContent,
|
||||
},
|
||||
);
|
||||
const confirmationDialog = Drupal.dialog(`<div>${message}</div>`, {
|
||||
title: Drupal.t('Change text format?'),
|
||||
classes: {
|
||||
'ui-dialog': 'editor-change-text-format-modal',
|
||||
},
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: Drupal.t('Continue'),
|
||||
class: 'button button--primary',
|
||||
click() {
|
||||
changeTextEditor(field, newFormatID);
|
||||
confirmationDialog.close();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: Drupal.t('Cancel'),
|
||||
class: 'button',
|
||||
click() {
|
||||
// Restore the active format ID: cancel changing text format. We
|
||||
// cannot simply call event.preventDefault() because jQuery's
|
||||
// change event is only triggered after the change has already
|
||||
// been accepted.
|
||||
select.value = activeFormatID;
|
||||
const eventChange = new Event('change');
|
||||
select.dispatchEvent(eventChange);
|
||||
confirmationDialog.close();
|
||||
},
|
||||
},
|
||||
],
|
||||
// Prevent this modal from being closed without the user making a choice
|
||||
// as per http://stackoverflow.com/a/5438771.
|
||||
closeOnEscape: false,
|
||||
create() {
|
||||
$(this).parent().find('.ui-dialog-titlebar-close').remove();
|
||||
},
|
||||
beforeClose: false,
|
||||
close(event) {
|
||||
// Automatically destroy the DOM element that was used for the dialog.
|
||||
$(event.target).remove();
|
||||
},
|
||||
});
|
||||
|
||||
confirmationDialog.showModal();
|
||||
} else {
|
||||
changeTextEditor(field, newFormatID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an empty object for editors to place their attachment code.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
Drupal.editors = {};
|
||||
|
||||
/**
|
||||
* Enables editors on text_format elements.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches an editor to an input element.
|
||||
* @prop {Drupal~behaviorDetach} detach
|
||||
* Detaches an editor from an input element.
|
||||
*/
|
||||
Drupal.behaviors.editor = {
|
||||
attach(context, settings) {
|
||||
// If there are no editor settings, there are no editors to enable.
|
||||
if (!settings.editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
once('editor', '[data-editor-for]', context).forEach((editor) => {
|
||||
const $this = $(editor);
|
||||
const field = findFieldForFormatSelector($this);
|
||||
|
||||
// Opt-out if no supported text area was found.
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the current active format.
|
||||
const activeFormatID = editor.value;
|
||||
field.setAttribute('data-editor-active-text-format', activeFormatID);
|
||||
|
||||
// Directly attach this text editor, if the text format is enabled.
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
// XSS protection for the current text format/editor is performed on
|
||||
// the server side, so we don't need to do anything special here.
|
||||
Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
|
||||
}
|
||||
// When there is no text editor for this text format, still track
|
||||
// changes, because the user has the ability to switch to some text
|
||||
// editor, otherwise this code would not be executed.
|
||||
$(field).on('change.editor keypress.editor', () => {
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
// Just knowing that the value was changed is enough, stop tracking.
|
||||
$(field).off('.editor');
|
||||
});
|
||||
|
||||
// Attach onChange handler to text format selector element.
|
||||
if (editor.tagName === 'SELECT') {
|
||||
$this.on('change.editorAttach', { field }, onTextFormatChange);
|
||||
}
|
||||
// Detach any editor when the containing form is submitted.
|
||||
$(field.form).on('submit', (event) => {
|
||||
// Do not detach if the event was canceled.
|
||||
if (event.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
// Detach the current editor (if any).
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
settings.editor.formats[activeFormatID],
|
||||
'serialize',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
detach(context, settings, trigger) {
|
||||
let editors;
|
||||
// The 'serialize' trigger indicates that we should simply update the
|
||||
// underlying element with the new text, without destroying the editor.
|
||||
if (trigger === 'serialize') {
|
||||
// Removing the editor-processed class guarantees that the editor will
|
||||
// be reattached. Only do this if we're planning to destroy the editor.
|
||||
editors = once.filter('editor', '[data-editor-for]', context);
|
||||
} else {
|
||||
editors = once.remove('editor', '[data-editor-for]', context);
|
||||
}
|
||||
|
||||
editors.forEach((editor) => {
|
||||
const $this = $(editor);
|
||||
const activeFormatID = editor.value;
|
||||
const field = findFieldForFormatSelector($this);
|
||||
if (field && activeFormatID in settings.editor.formats) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
settings.editor.formats[activeFormatID],
|
||||
trigger,
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches editor behaviors to the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
*
|
||||
* @listens event:change
|
||||
*
|
||||
* @fires event:formUpdated
|
||||
*/
|
||||
Drupal.editorAttach = function (field, format) {
|
||||
if (format.editor) {
|
||||
// Attach the text editor.
|
||||
Drupal.editors[format.editor].attach(field, format);
|
||||
|
||||
// Ensures form.js' 'formUpdated' event is triggered even for changes that
|
||||
// happen within the text editor.
|
||||
Drupal.editors[format.editor].onChange(field, () => {
|
||||
$(field).trigger('formUpdated');
|
||||
|
||||
// Keep track of changes, so we know what to do when switching text
|
||||
// formats and guaranteeing XSS protection.
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches editor behaviors from the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} trigger
|
||||
* Trigger value from the detach behavior.
|
||||
*/
|
||||
Drupal.editorDetach = function (field, format, trigger) {
|
||||
if (format.editor) {
|
||||
Drupal.editors[format.editor].detach(field, format, trigger);
|
||||
|
||||
// Restore the original value if the user didn't make any changes yet.
|
||||
if (field.getAttribute('data-editor-value-is-changed') === 'false') {
|
||||
field.value = field.getAttribute('data-editor-value-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
Reference in New Issue
Block a user