3 * Text editor-based in-place editor for formatted text content in Drupal.
5 * Depends on editor.module. Works with any (WYSIWYG) editor that implements the
6 * editor.js API, including the optional attachInlineEditor() and onChange()
8 * For example, assuming that a hypothetical editor's name was "Magical Editor"
9 * and its editor.js API implementation lived at Drupal.editors.magical, this
10 * JavaScript would use:
11 * - Drupal.editors.magical.attachInlineEditor()
14 (function ($, Drupal, drupalSettings, _) {
18 Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.editor# */{
21 * The text format for this field.
28 * Indicates whether this text format has transformations.
32 textFormatHasTransformations: null,
35 * Stores a reference to the text editor object for this field.
37 * @type {Drupal.quickedit.EditorModel}
42 * Stores the textual DOM element that is being in-place edited.
51 * @augments Drupal.quickedit.EditorView
53 * @param {object} options
54 * Options for the editor view.
56 initialize: function (options) {
57 Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
59 var metadata = Drupal.quickedit.metadata.get(this.fieldModel.get('fieldID'), 'custom');
60 this.textFormat = drupalSettings.editor.formats[metadata.format];
61 this.textFormatHasTransformations = metadata.formatHasTransformations;
62 this.textEditor = Drupal.editors[this.textFormat.editor];
64 // Store the actual value of this field. We'll need this to restore the
65 // original value when the user discards his modifications.
66 var $fieldItems = this.$el.find('.quickedit-field');
67 if ($fieldItems.length) {
68 this.$textElement = $fieldItems.eq(0);
71 this.$textElement = this.$el;
73 this.model.set('originalValue', this.$textElement.html());
80 * The text element edited.
82 getEditedElement: function () {
83 return this.$textElement;
89 * @param {object} fieldModel
91 * @param {string} state
94 stateChange: function (fieldModel, state) {
95 var editorModel = this.model;
96 var from = fieldModel.previous('state');
103 // Detach the text editor when entering the 'candidate' state from one
104 // of the states where it could have been attached.
105 if (from !== 'inactive' && from !== 'highlighted') {
106 this.textEditor.detach(this.$textElement.get(0), this.textFormat);
108 // A field model's editor view revert() method is invoked when an
109 // 'active' field becomes a 'candidate' field. But, in the case of
110 // this in-place editor, the content will have been *replaced* if the
111 // text format has transformation filters. Therefore, if we stop
112 // in-place editing this entity, revert explicitly.
113 if (from === 'active' && this.textFormatHasTransformations) {
116 if (from === 'invalid') {
117 this.removeValidationErrors();
125 // When transformation filters have been applied to the formatted text
126 // of this field, then we'll need to load a re-formatted version of it
127 // without the transformation filters.
128 if (this.textFormatHasTransformations) {
129 var $textElement = this.$textElement;
130 this._getUntransformedText(function (untransformedText) {
131 $textElement.html(untransformedText);
132 fieldModel.set('state', 'active');
135 // When no transformation filters have been applied: start WYSIWYG
136 // editing immediately!
138 // Defer updating the model until the current state change has
139 // propagated, to not trigger a nested state change event.
140 _.defer(function () {
141 fieldModel.set('state', 'active');
147 var textElement = this.$textElement.get(0);
148 var toolbarView = fieldModel.toolbarView;
149 this.textEditor.attachInlineEditor(
152 toolbarView.getMainWysiwygToolgroupId(),
153 toolbarView.getFloatedWysiwygToolgroupId()
155 // Set the state to 'changed' whenever the content has changed.
156 this.textEditor.onChange(textElement, function (htmlText) {
157 editorModel.set('currentValue', htmlText);
158 fieldModel.set('state', 'changed');
166 if (from === 'invalid') {
167 this.removeValidationErrors();
176 this.showValidationErrors();
185 * The sttings for the quick edit UI.
187 getQuickEditUISettings: function () {
188 return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: false};
194 revert: function () {
195 this.$textElement.html(this.model.get('originalValue'));
199 * Loads untransformed text for this field.
201 * More accurately: it re-filters formatted text to exclude transformation
202 * filters used by the text format.
204 * @param {function} callback
205 * A callback function that will receive the untransformed text.
207 * @see \Drupal\editor\Ajax\GetUntransformedTextCommand
209 _getUntransformedText: function (callback) {
210 var fieldID = this.fieldModel.get('fieldID');
212 // Create a Drupal.ajax instance to load the form.
213 var textLoaderAjax = Drupal.ajax({
214 url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('editor/!entity_type/!id/!field_name/!langcode/!view_mode')),
215 submit: {nocssjs: true}
218 // Implement a scoped editorGetUntransformedText AJAX command: calls the
220 textLoaderAjax.commands.editorGetUntransformedText = function (ajax, response, status) {
221 callback(response.data);
224 // This will ensure our scoped editorGetUntransformedText AJAX command
226 textLoaderAjax.execute();
231 })(jQuery, Drupal, drupalSettings, _);