@ckeditor/ckeditor5-html-embed 36.0.0 → 37.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,397 +2,326 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module html-embed/htmlembedediting
8
7
  */
9
-
10
8
  import { Plugin, icons } from 'ckeditor5/src/core';
11
9
  import { ButtonView } from 'ckeditor5/src/ui';
12
10
  import { toWidget } from 'ckeditor5/src/widget';
13
11
  import { logWarning, createElement } from 'ckeditor5/src/utils';
14
-
15
12
  import HtmlEmbedCommand from './htmlembedcommand';
16
-
17
13
  import '../theme/htmlembed.css';
18
-
19
14
  /**
20
15
  * The HTML embed editing feature.
21
- *
22
- * @extends module:core/plugin~Plugin
23
16
  */
24
17
  export default class HtmlEmbedEditing extends Plugin {
25
- /**
26
- * @inheritDoc
27
- */
28
- static get pluginName() {
29
- return 'HtmlEmbedEditing';
30
- }
31
-
32
- /**
33
- * @inheritDoc
34
- */
35
- constructor( editor ) {
36
- super( editor );
37
-
38
- editor.config.define( 'htmlEmbed', {
39
- showPreviews: false,
40
- sanitizeHtml: rawHtml => {
41
- /**
42
- * When using the HTML embed feature with the `htmlEmbed.showPreviews=true` option, it is strongly recommended to
43
- * define a sanitize function that will clean up the input HTML in order to avoid XSS vulnerability.
44
- *
45
- * For a detailed overview, check the {@glink features/html-embed HTML embed feature} documentation.
46
- *
47
- * @error html-embed-provide-sanitize-function
48
- */
49
- logWarning( 'html-embed-provide-sanitize-function' );
50
-
51
- return {
52
- html: rawHtml,
53
- hasChanged: false
54
- };
55
- }
56
- } );
57
-
58
- /**
59
- * Keeps references to {@link module:ui/button/buttonview~ButtonView edit, save, and cancel} button instances created for
60
- * each widget so they can be destroyed if they are no longer in DOM after the editing view was re-rendered.
61
- *
62
- * @private
63
- * @member {Set.<module:ui/button/buttonview~ButtonView>} #_widgetButtonViewReferences
64
- */
65
- this._widgetButtonViewReferences = new Set();
66
- }
67
-
68
- /**
69
- * @inheritDoc
70
- */
71
- init() {
72
- const editor = this.editor;
73
- const schema = editor.model.schema;
74
-
75
- schema.register( 'rawHtml', {
76
- inheritAllFrom: '$blockObject',
77
- allowAttributes: [ 'value' ]
78
- } );
79
-
80
- editor.commands.add( 'htmlEmbed', new HtmlEmbedCommand( editor ) );
81
-
82
- this._setupConversion();
83
- }
84
-
85
- /**
86
- * Prepares converters for the feature.
87
- *
88
- * @private
89
- */
90
- _setupConversion() {
91
- const editor = this.editor;
92
- const t = editor.t;
93
- const view = editor.editing.view;
94
- const widgetButtonViewReferences = this._widgetButtonViewReferences;
95
-
96
- const htmlEmbedConfig = editor.config.get( 'htmlEmbed' );
97
-
98
- // Destroy UI buttons created for widgets that have been removed from the view document (e.g. in the previous conversion).
99
- // This prevents unexpected memory leaks from UI views.
100
- this.editor.editing.view.on( 'render', () => {
101
- for ( const buttonView of widgetButtonViewReferences ) {
102
- if ( buttonView.element.isConnected ) {
103
- return;
104
- }
105
-
106
- buttonView.destroy();
107
- widgetButtonViewReferences.delete( buttonView );
108
- }
109
- }, { priority: 'lowest' } );
110
-
111
- // Register div.raw-html-embed as a raw content element so all of it's content will be provided
112
- // as a view element's custom property while data upcasting.
113
- editor.data.registerRawContentMatcher( {
114
- name: 'div',
115
- classes: 'raw-html-embed'
116
- } );
117
-
118
- editor.conversion.for( 'upcast' ).elementToElement( {
119
- view: {
120
- name: 'div',
121
- classes: 'raw-html-embed'
122
- },
123
- model: ( viewElement, { writer } ) => {
124
- // The div.raw-html-embed is registered as a raw content element,
125
- // so all it's content is available in a custom property.
126
- return writer.createElement( 'rawHtml', {
127
- value: viewElement.getCustomProperty( '$rawContent' )
128
- } );
129
- }
130
- } );
131
-
132
- editor.conversion.for( 'dataDowncast' ).elementToElement( {
133
- model: 'rawHtml',
134
- view: ( modelElement, { writer } ) => {
135
- return writer.createRawElement( 'div', { class: 'raw-html-embed' }, function( domElement ) {
136
- domElement.innerHTML = modelElement.getAttribute( 'value' ) || '';
137
- } );
138
- }
139
- } );
140
-
141
- editor.conversion.for( 'editingDowncast' ).elementToStructure( {
142
- model: { name: 'rawHtml', attributes: [ 'value' ] },
143
- view: ( modelElement, { writer } ) => {
144
- let domContentWrapper, state, props;
145
-
146
- const viewContentWrapper = writer.createRawElement( 'div', {
147
- class: 'raw-html-embed__content-wrapper'
148
- }, function( domElement ) {
149
- domContentWrapper = domElement;
150
-
151
- renderContent( { domElement, editor, state, props } );
152
-
153
- // Since there is a `data-cke-ignore-events` attribute set on the wrapper element in the editable mode,
154
- // the explicit `mousedown` handler on the `capture` phase is needed to move the selection onto the whole
155
- // HTML embed widget.
156
- domContentWrapper.addEventListener( 'mousedown', () => {
157
- if ( state.isEditable ) {
158
- const model = editor.model;
159
- const selectedElement = model.document.selection.getSelectedElement();
160
-
161
- // Move the selection onto the whole HTML embed widget if it's currently not selected.
162
- if ( selectedElement !== modelElement ) {
163
- model.change( writer => writer.setSelection( modelElement, 'on' ) );
164
- }
165
- }
166
- }, true );
167
- } );
168
-
169
- // API exposed on each raw HTML embed widget so other features can control a particular widget.
170
- const rawHtmlApi = {
171
- makeEditable() {
172
- state = Object.assign( {}, state, {
173
- isEditable: true
174
- } );
175
-
176
- renderContent( { domElement: domContentWrapper, editor, state, props } );
177
-
178
- view.change( writer => {
179
- writer.setAttribute( 'data-cke-ignore-events', 'true', viewContentWrapper );
180
- } );
181
-
182
- // This could be potentially pulled to a separate method called focusTextarea().
183
- domContentWrapper.querySelector( 'textarea' ).focus();
184
- },
185
- save( newValue ) {
186
- // If the value didn't change, we just cancel. If it changed,
187
- // it's enough to update the model – the entire widget will be reconverted.
188
- if ( newValue !== state.getRawHtmlValue() ) {
189
- editor.execute( 'htmlEmbed', newValue );
190
- editor.editing.view.focus();
191
- } else {
192
- this.cancel();
193
- }
194
- },
195
- cancel() {
196
- state = Object.assign( {}, state, {
197
- isEditable: false
198
- } );
199
-
200
- renderContent( { domElement: domContentWrapper, editor, state, props } );
201
- editor.editing.view.focus();
202
-
203
- view.change( writer => {
204
- writer.removeAttribute( 'data-cke-ignore-events', viewContentWrapper );
205
- } );
206
- }
207
- };
208
-
209
- state = {
210
- showPreviews: htmlEmbedConfig.showPreviews,
211
- isEditable: false,
212
- getRawHtmlValue: () => modelElement.getAttribute( 'value' ) || ''
213
- };
214
-
215
- props = {
216
- sanitizeHtml: htmlEmbedConfig.sanitizeHtml,
217
- textareaPlaceholder: t( 'Paste raw HTML here...' ),
218
-
219
- onEditClick() {
220
- rawHtmlApi.makeEditable();
221
- },
222
- onSaveClick( newValue ) {
223
- rawHtmlApi.save( newValue );
224
- },
225
- onCancelClick() {
226
- rawHtmlApi.cancel();
227
- }
228
- };
229
-
230
- const viewContainer = writer.createContainerElement( 'div', {
231
- class: 'raw-html-embed',
232
- 'data-html-embed-label': t( 'HTML snippet' ),
233
- dir: editor.locale.uiLanguageDirection
234
- }, viewContentWrapper );
235
-
236
- writer.setCustomProperty( 'rawHtmlApi', rawHtmlApi, viewContainer );
237
- writer.setCustomProperty( 'rawHtml', true, viewContainer );
238
-
239
- return toWidget( viewContainer, writer, {
240
- widgetLabel: t( 'HTML snippet' ),
241
- hasSelectionHandle: true
242
- } );
243
- }
244
- } );
245
-
246
- function renderContent( { domElement, editor, state, props } ) {
247
- // Remove all children;
248
- domElement.textContent = '';
249
-
250
- const domDocument = domElement.ownerDocument;
251
- let domTextarea;
252
-
253
- if ( state.isEditable ) {
254
- const textareaProps = {
255
- isDisabled: false,
256
- placeholder: props.textareaPlaceholder
257
- };
258
-
259
- domTextarea = createDomTextarea( { domDocument, state, props: textareaProps } );
260
-
261
- domElement.append( domTextarea );
262
- } else if ( state.showPreviews ) {
263
- const previewContainerProps = {
264
- sanitizeHtml: props.sanitizeHtml
265
- };
266
-
267
- domElement.append( createPreviewContainer( { domDocument, state, props: previewContainerProps, editor } ) );
268
- } else {
269
- const textareaProps = {
270
- isDisabled: true,
271
- placeholder: props.textareaPlaceholder
272
- };
273
-
274
- domElement.append( createDomTextarea( { domDocument, state, props: textareaProps } ) );
275
- }
276
-
277
- const buttonsWrapperProps = {
278
- onEditClick: props.onEditClick,
279
- onSaveClick: () => {
280
- props.onSaveClick( domTextarea.value );
281
- },
282
- onCancelClick: props.onCancelClick
283
- };
284
-
285
- domElement.prepend( createDomButtonsWrapper( { editor, domDocument, state, props: buttonsWrapperProps } ) );
286
- }
287
-
288
- function createDomButtonsWrapper( { editor, domDocument, state, props } ) {
289
- const domButtonsWrapper = createElement( domDocument, 'div', {
290
- class: 'raw-html-embed__buttons-wrapper'
291
- } );
292
-
293
- if ( state.isEditable ) {
294
- const saveButtonView = createUIButton( editor, 'save', props.onSaveClick );
295
- const cancelButtonView = createUIButton( editor, 'cancel', props.onCancelClick );
296
-
297
- domButtonsWrapper.append( saveButtonView.element, cancelButtonView.element );
298
- widgetButtonViewReferences.add( saveButtonView ).add( cancelButtonView );
299
- } else {
300
- const editButtonView = createUIButton( editor, 'edit', props.onEditClick );
301
-
302
- domButtonsWrapper.append( editButtonView.element );
303
- widgetButtonViewReferences.add( editButtonView );
304
- }
305
-
306
- return domButtonsWrapper;
307
- }
308
-
309
- function createDomTextarea( { domDocument, state, props } ) {
310
- const domTextarea = createElement( domDocument, 'textarea', {
311
- placeholder: props.placeholder,
312
- class: 'ck ck-reset ck-input ck-input-text raw-html-embed__source'
313
- } );
314
-
315
- domTextarea.disabled = props.isDisabled;
316
- domTextarea.value = state.getRawHtmlValue();
317
-
318
- return domTextarea;
319
- }
320
-
321
- function createPreviewContainer( { domDocument, state, props, editor } ) {
322
- const sanitizedOutput = props.sanitizeHtml( state.getRawHtmlValue() );
323
- const placeholderText = state.getRawHtmlValue().length > 0 ?
324
- t( 'No preview available' ) :
325
- t( 'Empty snippet content' );
326
-
327
- const domPreviewPlaceholder = createElement( domDocument, 'div', {
328
- class: 'ck ck-reset_all raw-html-embed__preview-placeholder'
329
- }, placeholderText );
330
-
331
- const domPreviewContent = createElement( domDocument, 'div', {
332
- class: 'raw-html-embed__preview-content',
333
- dir: editor.locale.contentLanguageDirection
334
- } );
335
-
336
- // Creating a contextual document fragment allows executing scripts when inserting into the preview element.
337
- // See: #8326.
338
- const domRange = domDocument.createRange();
339
- const domDocumentFragment = domRange.createContextualFragment( sanitizedOutput.html );
340
-
341
- domPreviewContent.appendChild( domDocumentFragment );
342
-
343
- const domPreviewContainer = createElement( domDocument, 'div', {
344
- class: 'raw-html-embed__preview'
345
- }, [
346
- domPreviewPlaceholder, domPreviewContent
347
- ] );
348
-
349
- return domPreviewContainer;
350
- }
351
- }
18
+ /**
19
+ * @inheritDoc
20
+ */
21
+ static get pluginName() {
22
+ return 'HtmlEmbedEditing';
23
+ }
24
+ /**
25
+ * @inheritDoc
26
+ */
27
+ constructor(editor) {
28
+ super(editor);
29
+ /**
30
+ * Keeps references to {@link module:ui/button/buttonview~ButtonView edit, save, and cancel} button instances created for
31
+ * each widget so they can be destroyed if they are no longer in DOM after the editing view was re-rendered.
32
+ */
33
+ this._widgetButtonViewReferences = new Set();
34
+ editor.config.define('htmlEmbed', {
35
+ showPreviews: false,
36
+ sanitizeHtml: rawHtml => {
37
+ /**
38
+ * When using the HTML embed feature with the `htmlEmbed.showPreviews=true` option, it is strongly recommended to
39
+ * define a sanitize function that will clean up the input HTML in order to avoid XSS vulnerability.
40
+ *
41
+ * For a detailed overview, check the {@glink features/html-embed HTML embed feature} documentation.
42
+ *
43
+ * @error html-embed-provide-sanitize-function
44
+ */
45
+ logWarning('html-embed-provide-sanitize-function');
46
+ return {
47
+ html: rawHtml,
48
+ hasChanged: false
49
+ };
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * @inheritDoc
55
+ */
56
+ init() {
57
+ const editor = this.editor;
58
+ const schema = editor.model.schema;
59
+ schema.register('rawHtml', {
60
+ inheritAllFrom: '$blockObject',
61
+ allowAttributes: ['value']
62
+ });
63
+ editor.commands.add('htmlEmbed', new HtmlEmbedCommand(editor));
64
+ this._setupConversion();
65
+ }
66
+ /**
67
+ * Prepares converters for the feature.
68
+ */
69
+ _setupConversion() {
70
+ const editor = this.editor;
71
+ const t = editor.t;
72
+ const view = editor.editing.view;
73
+ const widgetButtonViewReferences = this._widgetButtonViewReferences;
74
+ const htmlEmbedConfig = editor.config.get('htmlEmbed');
75
+ // Destroy UI buttons created for widgets that have been removed from the view document (e.g. in the previous conversion).
76
+ // This prevents unexpected memory leaks from UI views.
77
+ this.editor.editing.view.on('render', () => {
78
+ for (const buttonView of widgetButtonViewReferences) {
79
+ if (buttonView.element && buttonView.element.isConnected) {
80
+ return;
81
+ }
82
+ buttonView.destroy();
83
+ widgetButtonViewReferences.delete(buttonView);
84
+ }
85
+ }, { priority: 'lowest' });
86
+ // Register div.raw-html-embed as a raw content element so all of it's content will be provided
87
+ // as a view element's custom property while data upcasting.
88
+ editor.data.registerRawContentMatcher({
89
+ name: 'div',
90
+ classes: 'raw-html-embed'
91
+ });
92
+ editor.conversion.for('upcast').elementToElement({
93
+ view: {
94
+ name: 'div',
95
+ classes: 'raw-html-embed'
96
+ },
97
+ model: (viewElement, { writer }) => {
98
+ // The div.raw-html-embed is registered as a raw content element,
99
+ // so all it's content is available in a custom property.
100
+ return writer.createElement('rawHtml', {
101
+ value: viewElement.getCustomProperty('$rawContent')
102
+ });
103
+ }
104
+ });
105
+ editor.conversion.for('dataDowncast').elementToElement({
106
+ model: 'rawHtml',
107
+ view: (modelElement, { writer }) => {
108
+ return writer.createRawElement('div', { class: 'raw-html-embed' }, function (domElement) {
109
+ domElement.innerHTML = modelElement.getAttribute('value') || '';
110
+ });
111
+ }
112
+ });
113
+ editor.conversion.for('editingDowncast').elementToStructure({
114
+ model: { name: 'rawHtml', attributes: ['value'] },
115
+ view: (modelElement, { writer }) => {
116
+ let domContentWrapper;
117
+ let state;
118
+ let props;
119
+ const viewContentWrapper = writer.createRawElement('div', {
120
+ class: 'raw-html-embed__content-wrapper'
121
+ }, function (domElement) {
122
+ domContentWrapper = domElement;
123
+ renderContent({ editor, domElement, state, props });
124
+ // Since there is a `data-cke-ignore-events` attribute set on the wrapper element in the editable mode,
125
+ // the explicit `mousedown` handler on the `capture` phase is needed to move the selection onto the whole
126
+ // HTML embed widget.
127
+ domContentWrapper.addEventListener('mousedown', () => {
128
+ if (state.isEditable) {
129
+ const model = editor.model;
130
+ const selectedElement = model.document.selection.getSelectedElement();
131
+ // Move the selection onto the whole HTML embed widget if it's currently not selected.
132
+ if (selectedElement !== modelElement) {
133
+ model.change(writer => writer.setSelection(modelElement, 'on'));
134
+ }
135
+ }
136
+ }, true);
137
+ });
138
+ // API exposed on each raw HTML embed widget so other features can control a particular widget.
139
+ const rawHtmlApi = {
140
+ makeEditable() {
141
+ state = Object.assign({}, state, {
142
+ isEditable: true
143
+ });
144
+ renderContent({ domElement: domContentWrapper, editor, state, props });
145
+ view.change(writer => {
146
+ writer.setAttribute('data-cke-ignore-events', 'true', viewContentWrapper);
147
+ });
148
+ // This could be potentially pulled to a separate method called focusTextarea().
149
+ domContentWrapper.querySelector('textarea').focus();
150
+ },
151
+ save(newValue) {
152
+ // If the value didn't change, we just cancel. If it changed,
153
+ // it's enough to update the model – the entire widget will be reconverted.
154
+ if (newValue !== state.getRawHtmlValue()) {
155
+ editor.execute('htmlEmbed', newValue);
156
+ editor.editing.view.focus();
157
+ }
158
+ else {
159
+ this.cancel();
160
+ }
161
+ },
162
+ cancel() {
163
+ state = Object.assign({}, state, {
164
+ isEditable: false
165
+ });
166
+ renderContent({ domElement: domContentWrapper, editor, state, props });
167
+ editor.editing.view.focus();
168
+ view.change(writer => {
169
+ writer.removeAttribute('data-cke-ignore-events', viewContentWrapper);
170
+ });
171
+ }
172
+ };
173
+ state = {
174
+ showPreviews: htmlEmbedConfig.showPreviews,
175
+ isEditable: false,
176
+ getRawHtmlValue: () => modelElement.getAttribute('value') || ''
177
+ };
178
+ props = {
179
+ sanitizeHtml: htmlEmbedConfig.sanitizeHtml,
180
+ textareaPlaceholder: t('Paste raw HTML here...'),
181
+ onEditClick() {
182
+ rawHtmlApi.makeEditable();
183
+ },
184
+ onSaveClick(newValue) {
185
+ rawHtmlApi.save(newValue);
186
+ },
187
+ onCancelClick() {
188
+ rawHtmlApi.cancel();
189
+ }
190
+ };
191
+ const viewContainer = writer.createContainerElement('div', {
192
+ class: 'raw-html-embed',
193
+ 'data-html-embed-label': t('HTML snippet'),
194
+ dir: editor.locale.uiLanguageDirection
195
+ }, viewContentWrapper);
196
+ writer.setCustomProperty('rawHtmlApi', rawHtmlApi, viewContainer);
197
+ writer.setCustomProperty('rawHtml', true, viewContainer);
198
+ return toWidget(viewContainer, writer, {
199
+ label: t('HTML snippet'),
200
+ hasSelectionHandle: true
201
+ });
202
+ }
203
+ });
204
+ function renderContent({ editor, domElement, state, props }) {
205
+ // Remove all children;
206
+ domElement.textContent = '';
207
+ const domDocument = domElement.ownerDocument;
208
+ let domTextarea;
209
+ if (state.isEditable) {
210
+ const textareaProps = {
211
+ isDisabled: false,
212
+ placeholder: props.textareaPlaceholder
213
+ };
214
+ domTextarea = createDomTextarea({ domDocument, state, props: textareaProps });
215
+ domElement.append(domTextarea);
216
+ }
217
+ else if (state.showPreviews) {
218
+ const previewContainerProps = {
219
+ sanitizeHtml: props.sanitizeHtml
220
+ };
221
+ domElement.append(createPreviewContainer({ domDocument, state, props: previewContainerProps, editor }));
222
+ }
223
+ else {
224
+ const textareaProps = {
225
+ isDisabled: true,
226
+ placeholder: props.textareaPlaceholder
227
+ };
228
+ domElement.append(createDomTextarea({ domDocument, state, props: textareaProps }));
229
+ }
230
+ const buttonsWrapperProps = {
231
+ onEditClick: props.onEditClick,
232
+ onSaveClick: () => {
233
+ props.onSaveClick(domTextarea.value);
234
+ },
235
+ onCancelClick: props.onCancelClick
236
+ };
237
+ domElement.prepend(createDomButtonsWrapper({ editor, domDocument, state, props: buttonsWrapperProps }));
238
+ }
239
+ function createDomButtonsWrapper({ editor, domDocument, state, props }) {
240
+ const domButtonsWrapper = createElement(domDocument, 'div', {
241
+ class: 'raw-html-embed__buttons-wrapper'
242
+ });
243
+ if (state.isEditable) {
244
+ const saveButtonView = createUIButton(editor, 'save', props.onSaveClick);
245
+ const cancelButtonView = createUIButton(editor, 'cancel', props.onCancelClick);
246
+ domButtonsWrapper.append(saveButtonView.element, cancelButtonView.element);
247
+ widgetButtonViewReferences.add(saveButtonView).add(cancelButtonView);
248
+ }
249
+ else {
250
+ const editButtonView = createUIButton(editor, 'edit', props.onEditClick);
251
+ domButtonsWrapper.append(editButtonView.element);
252
+ widgetButtonViewReferences.add(editButtonView);
253
+ }
254
+ return domButtonsWrapper;
255
+ }
256
+ function createDomTextarea({ domDocument, state, props }) {
257
+ const domTextarea = createElement(domDocument, 'textarea', {
258
+ placeholder: props.placeholder,
259
+ class: 'ck ck-reset ck-input ck-input-text raw-html-embed__source'
260
+ });
261
+ domTextarea.disabled = props.isDisabled;
262
+ domTextarea.value = state.getRawHtmlValue();
263
+ return domTextarea;
264
+ }
265
+ function createPreviewContainer({ editor, domDocument, state, props }) {
266
+ const sanitizedOutput = props.sanitizeHtml(state.getRawHtmlValue());
267
+ const placeholderText = state.getRawHtmlValue().length > 0 ?
268
+ t('No preview available') :
269
+ t('Empty snippet content');
270
+ const domPreviewPlaceholder = createElement(domDocument, 'div', {
271
+ class: 'ck ck-reset_all raw-html-embed__preview-placeholder'
272
+ }, placeholderText);
273
+ const domPreviewContent = createElement(domDocument, 'div', {
274
+ class: 'raw-html-embed__preview-content',
275
+ dir: editor.locale.contentLanguageDirection
276
+ });
277
+ // Creating a contextual document fragment allows executing scripts when inserting into the preview element.
278
+ // See: #8326.
279
+ const domRange = domDocument.createRange();
280
+ const domDocumentFragment = domRange.createContextualFragment(sanitizedOutput.html);
281
+ domPreviewContent.appendChild(domDocumentFragment);
282
+ const domPreviewContainer = createElement(domDocument, 'div', {
283
+ class: 'raw-html-embed__preview'
284
+ }, [
285
+ domPreviewPlaceholder, domPreviewContent
286
+ ]);
287
+ return domPreviewContainer;
288
+ }
289
+ }
352
290
  }
353
-
354
- // Returns a UI button view that can be used in conversion.
355
- //
356
- // @param {module:utils/locale~Locale} locale Editor locale.
357
- // @param {'edit'|'save'|'cancel'} type Type of button to create.
358
- // @param {Function} onClick The callback executed on button click.
359
- // @returns {module:ui/button/buttonview~ButtonView}
360
- function createUIButton( editor, type, onClick ) {
361
- const t = editor.locale.t;
362
- const buttonView = new ButtonView( editor.locale );
363
- const command = editor.commands.get( 'htmlEmbed' );
364
-
365
- buttonView.set( {
366
- class: `raw-html-embed__${ type }-button`,
367
- icon: icons.pencil,
368
- tooltip: true,
369
- tooltipPosition: editor.locale.uiLanguageDirection === 'rtl' ? 'e' : 'w'
370
- } );
371
-
372
- buttonView.render();
373
-
374
- if ( type === 'edit' ) {
375
- buttonView.set( {
376
- icon: icons.pencil,
377
- label: t( 'Edit source' )
378
- } );
379
-
380
- buttonView.bind( 'isEnabled' ).to( command );
381
- } else if ( type === 'save' ) {
382
- buttonView.set( {
383
- icon: icons.check,
384
- label: t( 'Save changes' )
385
- } );
386
-
387
- buttonView.bind( 'isEnabled' ).to( command );
388
- } else {
389
- buttonView.set( {
390
- icon: icons.cancel,
391
- label: t( 'Cancel' )
392
- } );
393
- }
394
-
395
- buttonView.on( 'execute', onClick );
396
-
397
- return buttonView;
291
+ /**
292
+ * Returns a UI button view that can be used in conversion.
293
+ */
294
+ function createUIButton(editor, type, onClick) {
295
+ const { t } = editor.locale;
296
+ const buttonView = new ButtonView(editor.locale);
297
+ const command = editor.commands.get('htmlEmbed');
298
+ buttonView.set({
299
+ class: `raw-html-embed__${type}-button`,
300
+ icon: icons.pencil,
301
+ tooltip: true,
302
+ tooltipPosition: editor.locale.uiLanguageDirection === 'rtl' ? 'e' : 'w'
303
+ });
304
+ buttonView.render();
305
+ if (type === 'edit') {
306
+ buttonView.set({
307
+ icon: icons.pencil,
308
+ label: t('Edit source')
309
+ });
310
+ buttonView.bind('isEnabled').to(command);
311
+ }
312
+ else if (type === 'save') {
313
+ buttonView.set({
314
+ icon: icons.check,
315
+ label: t('Save changes')
316
+ });
317
+ buttonView.bind('isEnabled').to(command);
318
+ }
319
+ else {
320
+ buttonView.set({
321
+ icon: icons.cancel,
322
+ label: t('Cancel')
323
+ });
324
+ }
325
+ buttonView.on('execute', onClick);
326
+ return buttonView;
398
327
  }