ckeditor5 1.9.0 → 1.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb520612d3f2847e8d70c48a8ffb3d1ad4be25800dd5f141a6948caf30b07978
4
- data.tar.gz: 5f9850fb6a39774ab796f67b5dc95fa0ec1e6a5fe6f04fb3481f6ff17c76e026
3
+ metadata.gz: e3e06c4a4882429855d9570658be64f10fffd97371d0ae60e9aee2ecec08df00
4
+ data.tar.gz: f48eabec0f1bcab9299be0f00094f2e1078a757582a705e57d504748943b5790
5
5
  SHA512:
6
- metadata.gz: 7057a3e537f5626b4754b815c1f418ddac64301350aa3b0299d5051dcda474a5f397018389a0e7c773bc1d15687043498a2bcd44518581e4233f8ab4ae85dc52
7
- data.tar.gz: 0526b5810f411971ce3adafa1bad252eabaffc566f3d637b6585c2a16a6d6c938078e8b88ed88b34e610477c87c66f5dfc74d092fab2f46f183327f2fca9d287
6
+ metadata.gz: c989f7723dadc126c356b579f50160b09f3c359af8a791a919bcff0595c7387241f89f5a63a100c482878d0f067c094f45619628094d6a8c0659d797d75cb514
7
+ data.tar.gz: a411bbde271725fbaeddfc58023307837ffac5e26d4814ed13e922288846129973a2b297681076b480982485a91ed4b5c527056cf2b9b799cfbb321a5ed83762
data/README.md CHANGED
@@ -102,8 +102,8 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
102
102
  - [Balloon editor 🎈](#balloon-editor-)
103
103
  - [Decoupled editor 🌐](#decoupled-editor-)
104
104
  - [Using Context 📦](#using-context-)
105
- - [Benefits of Using Context in Collaboration 🤝](#benefits-of-using-context-in-collaboration-)
106
105
  - [Using Context in CKEditor 5 🔄](#using-context-in-ckeditor-5-)
106
+ - [Example usage of `ckeditor5_context` helper 📝](#example-usage-of-ckeditor5_context-helper-)
107
107
  - [How to access editor instance? 🤔](#how-to-access-editor-instance-)
108
108
  - [Common Tasks and Solutions 💡](#common-tasks-and-solutions-)
109
109
  - [Setting Editor Language 🌐](#setting-editor-language-)
@@ -115,6 +115,8 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
115
115
  - [Events fired by the editor 🔊](#events-fired-by-the-editor-)
116
116
  - [`editor-ready` event](#editor-ready-event)
117
117
  - [`editor-error` event](#editor-error-event)
118
+ - [`editor-change` event](#editor-change-event)
119
+ - [Inline event handling](#inline-event-handling)
118
120
  - [Trademarks 📜](#trademarks-)
119
121
  - [License 📜](#license-)
120
122
 
@@ -967,25 +969,24 @@ If you want to use a decoupled editor, you can pass the `type` keyword argument
967
969
 
968
970
  Context CKEditor 5 is a feature that allows multiple editor instances to share a common configuration and state. This is particularly useful in collaborative environments where multiple users are editing different parts of the same document simultaneously. By using a shared context, all editor instances can synchronize their configurations, plugins, and other settings, ensuring a consistent editing experience across all users.
969
971
 
970
- ### Benefits of Using Context in Collaboration 🤝
971
-
972
- 1. **Consistency**: Ensures that all editor instances have the same configuration, plugins, and settings, providing a uniform editing experience.
973
- 2. **Synchronization**: Allows real-time synchronization of content and changes across multiple editor instances, making collaborative editing seamless.
974
- 3. **Resource Sharing**: Reduces the overhead of loading and initializing multiple editor instances by sharing common resources and configurations.
975
- 4. **Simplified Management**: Makes it easier to manage and update the configuration and state of multiple editor instances from a single point.
972
+ ![CKEditor 5 Context](docs/context.png)
976
973
 
977
974
  ### Using Context in CKEditor 5 🔄
978
975
 
979
976
  Format of the `ckeditor5_context` helper:
980
977
 
981
978
  ```erb
982
- <%= ckeditor5_context config: { ...you context config... }, plugins: [ ...your context plugins... ] do %>
979
+ <!-- app/views/demos/index.html.erb -->
980
+
981
+ <%= ckeditor5_context config: { ... }, plugins: [ ... ] do %>
983
982
  <%= ckeditor5_editor %>
984
983
  <%= ckeditor5_editor %>
985
984
  <% end %>
986
985
  ```
987
986
 
988
- Example usage:
987
+ The `ckeditor5_context` helper takes the `config` and `plugins` keyword arguments. The `config` keyword argument allows you to define the shared configuration of the editor instances, while the `plugins` keyword argument allows you to define the shared plugins. Format of these arguments is the same as in the `ckeditor5_editor` helper.
988
+
989
+ ### Example usage of `ckeditor5_context` helper 📝
989
990
 
990
991
  ```erb
991
992
  <!-- app/views/demos/index.html.erb -->
@@ -1260,6 +1261,8 @@ class HighlightCommand extends Command {
1260
1261
 
1261
1262
  ## Events fired by the editor 🔊
1262
1263
 
1264
+ CKEditor 5 provides a set of events that you can listen to in order to react to changes in the editor. You can listen to these events using the `addEventListener` method or by defining event handlers directly in the view.
1265
+
1263
1266
  ### `editor-ready` event
1264
1267
 
1265
1268
  The event is fired when the initialization of the editor is completed. You can listen to it using the `editor-ready` event.
@@ -1280,6 +1283,40 @@ document.getElementById('editor').addEventListener('editor-error', () => {
1280
1283
  });
1281
1284
  ```
1282
1285
 
1286
+ ### `editor-change` event
1287
+
1288
+ The event is fired when the content of the editor changes. You can listen to it using the `editor-change` event.
1289
+
1290
+ ```js
1291
+ document.getElementById('editor').addEventListener('editor-change', () => {
1292
+ console.log('Editor content has changed');
1293
+ });
1294
+ ```
1295
+
1296
+ ### Inline event handling
1297
+
1298
+ You can also define event handlers directly in the view using the `oneditorchange`, `oneditorerror`, and `oneditorready` attributes.
1299
+
1300
+ ```erb
1301
+ <!-- app/views/demos/index.html.erb -->
1302
+
1303
+ <script type="text/javascript">
1304
+ function onEditorChange(event) {
1305
+ // event.detail.editor, event.detail.data
1306
+ }
1307
+
1308
+ function onEditorError(event) {
1309
+ // event.detail.editor
1310
+ }
1311
+
1312
+ function onEditorReady(event) {
1313
+ // event.detail.editor
1314
+ }
1315
+ </script>
1316
+
1317
+ <%= ckeditor5_editor style: 'width: 600px', id: 'editor', oneditorchange: 'onEditorChange', oneditorerror: 'onEditorError', oneditorready: 'onEditorReady' %>
1318
+ ```
1319
+
1283
1320
  ## Trademarks 📜
1284
1321
 
1285
1322
  CKEditor® is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com/) All rights reserved. For more information about the license of CKEditor® please visit [CKEditor's licensing page](https://ckeditor.com/legal/ckeditor-oss-license/).
@@ -1288,6 +1325,6 @@ This gem is not owned by CKSource and does not use the CKEditor® trademark for
1288
1325
 
1289
1326
  ## License 📜
1290
1327
 
1291
- This project is licensed under the terms of the [GNU General Public License v2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). See the [LICENSE](LICENSE) file for details.
1328
+ This project is licensed under the terms of the [GNU General Public License v2.0 or later](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). See the [LICENSE](LICENSE) file for details.
1292
1329
 
1293
- This project uses CKEditor 5 which is licensed under the terms of [GNU General Public License Version 2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). For more information about CKEditor 5 licensing, please see their [official documentation](https://ckeditor.com/legal/ckeditor-oss-license/).
1330
+ This project uses CKEditor 5 which is licensed under the terms of [GNU General Public License Version 2 or later](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). For more information about CKEditor 5 licensing, please see their [official documentation](https://ckeditor.com/legal/ckeditor-oss-license/).
@@ -3,9 +3,9 @@
3
3
  require 'uri'
4
4
  require 'action_view'
5
5
 
6
- module CKEditor5::Rails::Assets
7
- WEBCOMPONENT_SOURCE = File.read(File.join(__dir__, 'webcomponent.mjs')).html_safe
6
+ require_relative 'webcomponent_bundle'
8
7
 
8
+ module CKEditor5::Rails::Assets
9
9
  class AssetsBundleHtmlSerializer
10
10
  include ActionView::Helpers::TagHelper
11
11
 
@@ -38,7 +38,7 @@ module CKEditor5::Rails::Assets
38
38
  private
39
39
 
40
40
  def web_component_tag
41
- @web_component_tag ||= tag.script(WEBCOMPONENT_SOURCE, type: 'module', nonce: true)
41
+ @web_component_tag ||= tag.script(WebComponentBundle.source, type: 'module', nonce: true)
42
42
  end
43
43
 
44
44
  def window_scripts_tags
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails::Assets
4
+ module WebComponentBundle
5
+ WEBCOMPONENTS_PATH = File.join(__dir__, 'webcomponents')
6
+ WEBCOMPONENTS_MODULES = [
7
+ 'utils.mjs',
8
+ 'components/editable.mjs',
9
+ 'components/ui-part.mjs',
10
+ 'components/editor.mjs',
11
+ 'components/context.mjs'
12
+ ].freeze
13
+
14
+ module_function
15
+
16
+ def source
17
+ @source ||= WEBCOMPONENTS_MODULES.map do |file|
18
+ File.read(File.join(WEBCOMPONENTS_PATH, file))
19
+ end.join("\n").html_safe
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,113 @@
1
+ class CKEditorContextComponent extends HTMLElement {
2
+ static get observedAttributes() {
3
+ return ['plugins', 'config'];
4
+ }
5
+
6
+ /** @type {import('ckeditor5').Context|null} */
7
+ instance = null;
8
+
9
+ /** @type {Promise<import('ckeditor5').Context>} */
10
+ instancePromise = Promise.withResolvers();
11
+
12
+ /** @type {Set<CKEditorComponent>} */
13
+ #connectedEditors = new Set();
14
+
15
+ async connectedCallback() {
16
+ try {
17
+ execIfDOMReady(() => this.#initializeContext());
18
+ } catch (error) {
19
+ console.error('Failed to initialize context:', error);
20
+ this.dispatchEvent(new CustomEvent('context-error', { detail: error }));
21
+ }
22
+ }
23
+
24
+ async attributeChangedCallback(name, oldValue, newValue) {
25
+ if (oldValue !== null && oldValue !== newValue) {
26
+ await this.#initializeContext();
27
+ }
28
+ }
29
+
30
+ async disconnectedCallback() {
31
+ if (this.instance) {
32
+ await this.instance.destroy();
33
+ this.instance = null;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Register editor component with this context
39
+ *
40
+ * @param {CKEditorComponent} editor
41
+ */
42
+ registerEditor(editor) {
43
+ this.#connectedEditors.add(editor);
44
+ }
45
+
46
+ /**
47
+ * Unregister editor component from this context
48
+ *
49
+ * @param {CKEditorComponent} editor
50
+ */
51
+ unregisterEditor(editor) {
52
+ this.#connectedEditors.delete(editor);
53
+ }
54
+
55
+ /**
56
+ * Initialize CKEditor context with shared configuration
57
+ *
58
+ * @private
59
+ */
60
+ async #initializeContext() {
61
+ if (this.instance) {
62
+ this.instancePromise = Promise.withResolvers();
63
+
64
+ await this.instance.destroy();
65
+
66
+ this.instance = null;
67
+ }
68
+
69
+ const { Context, ContextWatchdog } = await import('ckeditor5');
70
+ const plugins = await this.#getPlugins();
71
+ const config = this.#getConfig();
72
+
73
+ this.instance = new ContextWatchdog(Context, {
74
+ crashNumberLimit: 10
75
+ });
76
+
77
+ await this.instance.create({
78
+ ...config,
79
+ plugins
80
+ });
81
+
82
+ this.instance.on('itemError', (...args) => {
83
+ console.error('Context item error:', ...args);
84
+ });
85
+
86
+ this.instancePromise.resolve(this.instance);
87
+ this.dispatchEvent(new CustomEvent('context-ready', { detail: this.instance }));
88
+
89
+ // Reinitialize connected editors.
90
+ await Promise.all(
91
+ [...this.#connectedEditors].map(editor => editor.reinitializeEditor())
92
+ );
93
+ }
94
+
95
+ async #getPlugins() {
96
+ const raw = this.getAttribute('plugins');
97
+
98
+ return loadAsyncImports(raw ? JSON.parse(raw) : []);
99
+ }
100
+
101
+ /**
102
+ * Gets context configuration with resolved element references.
103
+ *
104
+ * @private
105
+ */
106
+ #getConfig() {
107
+ const config = JSON.parse(this.getAttribute('config') || '{}');
108
+
109
+ return resolveElementReferences(config);
110
+ }
111
+ }
112
+
113
+ customElements.define('ckeditor-context-component', CKEditorContextComponent);
@@ -0,0 +1,113 @@
1
+ class CKEditorEditableComponent extends HTMLElement {
2
+ /**
3
+ * List of attributes that trigger updates when changed
4
+ *
5
+ * @static
6
+ * @returns {string[]} Array of attribute names to observe
7
+ */
8
+ static get observedAttributes() {
9
+ return ['name'];
10
+ }
11
+
12
+ /**
13
+ * Gets the name of this editable region
14
+ *
15
+ * @returns {string} The name attribute value
16
+ */
17
+ get name() {
18
+ // The default value is set mainly for decoupled editors where the name is not required.
19
+ return this.getAttribute('name') || 'editable';
20
+ }
21
+
22
+ /**
23
+ * Gets the actual editable DOM element
24
+ * @returns {HTMLDivElement|null} The div element containing editable content
25
+ */
26
+ get editableElement() {
27
+ return this.querySelector('div');
28
+ }
29
+
30
+ /**
31
+ * Lifecycle callback when element is added to DOM
32
+ * Sets up the editable element and registers it with the parent editor
33
+ *
34
+ * @throws {Error} If not used as child of ckeditor-component
35
+ */
36
+ connectedCallback() {
37
+ execIfDOMReady(() => {
38
+ const editorComponent = this.#queryEditorElement();
39
+
40
+ if (!editorComponent ) {
41
+ throw new Error('ckeditor-editable-component must be a child of ckeditor-component');
42
+ }
43
+
44
+ this.innerHTML = `<div>${this.innerHTML}</div>`;
45
+ this.style.display = 'block';
46
+
47
+ if (editorComponent.isDecoupled()) {
48
+ editorComponent.runAfterEditorReady(editor => {
49
+ this.appendChild(editor.ui.view[this.name].element);
50
+ });
51
+ } else {
52
+ if (!this.name) {
53
+ throw new Error('Editable component missing required "name" attribute');
54
+ }
55
+
56
+ editorComponent.editables[this.name] = this;
57
+ }
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Lifecycle callback for attribute changes
63
+ * Handles name changes and propagates other attributes to editable element
64
+ *
65
+ * @param {string} name - Name of changed attribute
66
+ * @param {string|null} oldValue - Previous value
67
+ * @param {string|null} newValue - New value
68
+ */
69
+ attributeChangedCallback(name, oldValue, newValue) {
70
+ if (oldValue === newValue) {
71
+ return;
72
+ }
73
+
74
+ if (name === 'name') {
75
+ if (!oldValue) {
76
+ return;
77
+ }
78
+
79
+ const editorComponent = this.#queryEditorElement();
80
+
81
+ if (editorComponent) {
82
+ editorComponent.editables[newValue] = editorComponent.editables[oldValue];
83
+ delete editorComponent.editables[oldValue];
84
+ }
85
+ } else {
86
+ this.editableElement.setAttribute(name, newValue);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Lifecycle callback when element is removed
92
+ * Un-registers this editable from the parent editor
93
+ */
94
+ disconnectedCallback() {
95
+ const editorComponent = this.#queryEditorElement();
96
+
97
+ if (editorComponent) {
98
+ delete editorComponent.editables[this.name];
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Finds the parent editor component
104
+ *
105
+ * @private
106
+ * @returns {CKEditorComponent|null} Parent editor component or null if not found
107
+ */
108
+ #queryEditorElement() {
109
+ return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
110
+ }
111
+ }
112
+
113
+ customElements.define('ckeditor-editable-component', CKEditorEditableComponent);
@@ -1,26 +1,3 @@
1
- /**
2
- * Custom web component for integrating CKEditor 5 into web applications.
3
- *
4
- * @class
5
- * @extends HTMLElement
6
- *
7
- * @property {import('ckeditor5').Editor|null} instance - The current CKEditor instance
8
- * @property {Record<string, HTMLElement>} editables - Object containing editable elements
9
- *
10
- * @fires editor-ready - Fired when editor is initialized with the editor instance as detail
11
- * @fires editor-error - Fired when initialization fails with the error as detail
12
- *
13
- * @example
14
- * // Basic usage with Classic Editor
15
- * <ckeditor-component type="ClassicEditor" config='{"toolbar": ["bold", "italic"]}'>
16
- * </ckeditor-component>
17
- *
18
- * // Multiroot editor with multiple editables
19
- * <ckeditor-component type="MultirootEditor">
20
- * <ckeditor-editable-component name="title">Title content</ckeditor-editable-component>
21
- * <ckeditor-editable-component name="content">Main content</ckeditor-editable-component>
22
- * </ckeditor-component>
23
- */
24
1
  class CKEditorComponent extends HTMLElement {
25
2
  /**
26
3
  * List of attributes that trigger updates when changed.
@@ -63,6 +40,69 @@ class CKEditorComponent extends HTMLElement {
63
40
  /** @type {String} ID of editor within context */
64
41
  #contextEditorId = null;
65
42
 
43
+ /** @type {(event: CustomEvent) => void} Event handler for editor change */
44
+ get oneditorchange() {
45
+ return this.#getEventHandler('editorchange');
46
+ }
47
+
48
+ set oneditorchange(handler) {
49
+ this.#setEventHandler('editorchange', handler);
50
+ }
51
+
52
+ /** @type {(event: CustomEvent) => void} Event handler for editor ready */
53
+ get oneditorready() {
54
+ return this.#getEventHandler('editorready');
55
+ }
56
+
57
+ set oneditorready(handler) {
58
+ this.#setEventHandler('editorready', handler);
59
+ }
60
+
61
+ /** @type {(event: CustomEvent) => void} Event handler for editor error */
62
+ get oneditorerror() {
63
+ return this.#getEventHandler('editorerror');
64
+ }
65
+
66
+ set oneditorerror(handler) {
67
+ this.#setEventHandler('editorerror', handler);
68
+ }
69
+
70
+ /**
71
+ * Gets event handler function from attribute or property
72
+ *
73
+ * @private
74
+ * @param {string} name - Event name without 'on' prefix
75
+ * @returns {Function|null} Event handler or null
76
+ */
77
+ #getEventHandler(name) {
78
+ if (this.hasAttribute(`on${name}`)) {
79
+ const handler = this.getAttribute(`on${name}`);
80
+
81
+ if (!isSafeKey(handler)) {
82
+ throw new Error(`Unsafe event handler attribute value: ${handler}`);
83
+ }
84
+
85
+ return window[handler] || new Function('event', handler);
86
+ }
87
+ return this[`#${name}Handler`];
88
+ }
89
+
90
+ /**
91
+ * Sets event handler function
92
+ *
93
+ * @private
94
+ * @param {string} name - Event name without 'on' prefix
95
+ * @param {Function|string|null} handler - Event handler
96
+ */
97
+ #setEventHandler(name, handler) {
98
+ if (typeof handler === 'string') {
99
+ this.setAttribute(`on${name}`, handler);
100
+ } else {
101
+ this.removeAttribute(`on${name}`);
102
+ this[`#${name}Handler`] = handler;
103
+ }
104
+ }
105
+
66
106
  /**
67
107
  * Lifecycle callback when element is connected to DOM
68
108
  * Initializes the editor when DOM is ready
@@ -83,7 +123,11 @@ class CKEditorComponent extends HTMLElement {
83
123
  });
84
124
  } catch (error) {
85
125
  console.error('Failed to initialize editor:', error);
86
- this.dispatchEvent(new CustomEvent('editor-error', { detail: error }));
126
+
127
+ const event = new CustomEvent('editor-error', { detail: error });
128
+
129
+ this.dispatchEvent(event);
130
+ this.oneditorerror?.(event);
87
131
  }
88
132
  }
89
133
 
@@ -250,8 +294,6 @@ class CKEditorComponent extends HTMLElement {
250
294
  instance = await Editor.create(content, config);
251
295
  }
252
296
 
253
- this.dispatchEvent(new CustomEvent('editor-ready', { detail: instance }));
254
-
255
297
  return {
256
298
  contextId,
257
299
  instance,
@@ -299,14 +341,50 @@ class CKEditorComponent extends HTMLElement {
299
341
 
300
342
  this.#setupContentSync();
301
343
  this.#setupEditableHeight();
344
+ this.#setupDataChangeListener();
302
345
 
303
346
  this.instancePromise.resolve(this.instance);
347
+
348
+ // Broadcast editor ready event
349
+ const event = new CustomEvent('editor-ready', { detail: this.instance });
350
+
351
+ this.dispatchEvent(event);
352
+ this.oneditorready?.(event);
304
353
  } catch (err) {
305
354
  this.instancePromise.reject(err);
306
355
  throw err;
307
356
  }
308
357
  }
309
358
 
359
+ /**
360
+ * Sets up data change listener that broadcasts content changes
361
+ *
362
+ * @private
363
+ */
364
+ #setupDataChangeListener() {
365
+ const getRootContent = rootName => this.instance.getData({ rootName });
366
+ const getAllRoots = () =>
367
+ this.instance.model.document
368
+ .getRootNames()
369
+ .reduce((acc, rootName) => ({
370
+ ...acc,
371
+ [rootName]: getRootContent(rootName)
372
+ }), {});
373
+
374
+ this.instance?.model.document.on('change:data', () => {
375
+ const event = new CustomEvent('editor-change', {
376
+ detail: {
377
+ editor: this.instance,
378
+ data: getAllRoots(),
379
+ },
380
+ bubbles: true
381
+ });
382
+
383
+ this.dispatchEvent(event);
384
+ this.oneditorchange?.(event);
385
+ });
386
+ }
387
+
310
388
  /**
311
389
  * Checks if current editor is classic type
312
390
  *
@@ -515,138 +593,6 @@ class CKEditorComponent extends HTMLElement {
515
593
  }
516
594
  }
517
595
 
518
- /**
519
- * Custom element that provides shared CKEditor context for multiple editors.
520
- *
521
- * @extends HTMLElement
522
- * @example
523
- *
524
- * <ckeditor-context-component plugins='[ ... ]'>
525
- * <ckeditor-component type="ClassicEditor" config='{"toolbar": ["bold", "italic"]}'>
526
- * <ckeditor-component type="ClassicEditor" config='{"toolbar": ["bold", "italic"]}'>
527
- * </ckeditor-component>
528
- */
529
- class CKEditorContextComponent extends HTMLElement {
530
- static get observedAttributes() {
531
- return ['plugins', 'config'];
532
- }
533
-
534
- /** @type {import('ckeditor5').Context|null} */
535
- instance = null;
536
-
537
- /** @type {Promise<import('ckeditor5').Context>} */
538
- instancePromise = Promise.withResolvers();
539
-
540
- /** @type {Set<CKEditorComponent>} */
541
- #connectedEditors = new Set();
542
-
543
- async connectedCallback() {
544
- try {
545
- execIfDOMReady(() => this.#initializeContext());
546
- } catch (error) {
547
- console.error('Failed to initialize context:', error);
548
- this.dispatchEvent(new CustomEvent('context-error', { detail: error }));
549
- }
550
- }
551
-
552
- async attributeChangedCallback(name, oldValue, newValue) {
553
- if (oldValue !== null && oldValue !== newValue) {
554
- await this.#initializeContext();
555
- }
556
- }
557
-
558
- async disconnectedCallback() {
559
- if (this.instance) {
560
- await this.instance.destroy();
561
- this.instance = null;
562
- }
563
- }
564
-
565
- /**
566
- * Register editor component with this context
567
- *
568
- * @param {CKEditorComponent} editor
569
- */
570
- registerEditor(editor) {
571
- this.#connectedEditors.add(editor);
572
- }
573
-
574
- /**
575
- * Unregister editor component from this context
576
- *
577
- * @param {CKEditorComponent} editor
578
- */
579
- unregisterEditor(editor) {
580
- this.#connectedEditors.delete(editor);
581
- }
582
-
583
- /**
584
- * Initialize CKEditor context with shared configuration
585
- *
586
- * @private
587
- */
588
- async #initializeContext() {
589
- if (this.instance) {
590
- this.instancePromise = Promise.withResolvers();
591
-
592
- await this.instance.destroy();
593
-
594
- this.instance = null;
595
- }
596
-
597
- const { Context, ContextWatchdog } = await import('ckeditor5');
598
- const plugins = await this.#getPlugins();
599
- const config = this.#getConfig();
600
-
601
- this.instance = new ContextWatchdog(Context, {
602
- crashNumberLimit: 10
603
- });
604
-
605
- await this.instance.create({
606
- ...config,
607
- plugins
608
- });
609
-
610
- this.instance.on('itemError', (...args) => {
611
- console.error('Context item error:', ...args);
612
- });
613
-
614
- this.instancePromise.resolve(this.instance);
615
- this.dispatchEvent(new CustomEvent('context-ready', { detail: this.instance }));
616
-
617
- // Reinitialize connected editors.
618
- await Promise.all(
619
- [...this.#connectedEditors].map(editor => editor.reinitializeEditor())
620
- );
621
- }
622
-
623
- async #getPlugins() {
624
- const raw = this.getAttribute('plugins');
625
-
626
- return loadAsyncImports(raw ? JSON.parse(raw) : []);
627
- }
628
-
629
- /**
630
- * Gets context configuration with resolved element references.
631
- *
632
- * @private
633
- */
634
- #getConfig() {
635
- const config = JSON.parse(this.getAttribute('config') || '{}');
636
-
637
- return resolveElementReferences(config);
638
- }
639
- }
640
-
641
- /**
642
- * Tracks and manages editable roots for CKEditor MultiRoot editor.
643
- * Provides a proxy-based API for dynamically managing editable elements with automatic
644
- * attachment/detachment of editor roots.
645
- *
646
- * @class
647
- * @property {CKEditorComponent} #editorElement - Reference to parent editor component
648
- * @property {Record<string, HTMLElement>} #editables - Map of tracked editable elements
649
- */
650
596
  class CKEditorMultiRootEditablesTracker {
651
597
  #editorElement;
652
598
  #editables;
@@ -765,325 +711,4 @@ class CKEditorMultiRootEditablesTracker {
765
711
  }
766
712
  }
767
713
 
768
- /**
769
- * Custom HTML element representing an editable region for CKEditor.
770
- * Must be used as a child of ckeditor-component element.
771
- *
772
- * @customElement ckeditor-editable-component
773
- * @extends HTMLElement
774
- *
775
- * @property {string} name - The name of the editable region, accessed via getAttribute
776
- * @property {HTMLDivElement} editableElement - The div element containing editable content
777
- *
778
- * @fires connectedCallback - When the element is added to the DOM
779
- * @fires attributeChangedCallback - When element attributes change
780
- * @fires disconnectedCallback - When the element is removed from the DOM
781
- *
782
- * @throws {Error} Throws error if not used as child of ckeditor-component
783
- *
784
- * @example
785
- * <ckeditor-component>
786
- * <ckeditor-editable-component name="main">
787
- * Content goes here
788
- * </ckeditor-editable-component>
789
- * </ckeditor-component>
790
- */
791
- class CKEditorEditableComponent extends HTMLElement {
792
- /**
793
- * List of attributes that trigger updates when changed
794
- *
795
- * @static
796
- * @returns {string[]} Array of attribute names to observe
797
- */
798
- static get observedAttributes() {
799
- return ['name'];
800
- }
801
-
802
- /**
803
- * Gets the name of this editable region
804
- *
805
- * @returns {string} The name attribute value
806
- */
807
- get name() {
808
- // The default value is set mainly for decoupled editors where the name is not required.
809
- return this.getAttribute('name') || 'editable';
810
- }
811
-
812
- /**
813
- * Gets the actual editable DOM element
814
- * @returns {HTMLDivElement|null} The div element containing editable content
815
- */
816
- get editableElement() {
817
- return this.querySelector('div');
818
- }
819
-
820
- /**
821
- * Lifecycle callback when element is added to DOM
822
- * Sets up the editable element and registers it with the parent editor
823
- *
824
- * @throws {Error} If not used as child of ckeditor-component
825
- */
826
- connectedCallback() {
827
- execIfDOMReady(() => {
828
- const editorComponent = this.#queryEditorElement();
829
-
830
- if (!editorComponent ) {
831
- throw new Error('ckeditor-editable-component must be a child of ckeditor-component');
832
- }
833
-
834
- this.innerHTML = `<div>${this.innerHTML}</div>`;
835
- this.style.display = 'block';
836
-
837
- if (editorComponent.isDecoupled()) {
838
- editorComponent.runAfterEditorReady(editor => {
839
- this.appendChild(editor.ui.view[this.name].element);
840
- });
841
- } else {
842
- if (!this.name) {
843
- throw new Error('Editable component missing required "name" attribute');
844
- }
845
-
846
- editorComponent.editables[this.name] = this;
847
- }
848
- });
849
- }
850
-
851
- /**
852
- * Lifecycle callback for attribute changes
853
- * Handles name changes and propagates other attributes to editable element
854
- *
855
- * @param {string} name - Name of changed attribute
856
- * @param {string|null} oldValue - Previous value
857
- * @param {string|null} newValue - New value
858
- */
859
- attributeChangedCallback(name, oldValue, newValue) {
860
- if (oldValue === newValue) {
861
- return;
862
- }
863
-
864
- if (name === 'name') {
865
- if (!oldValue) {
866
- return;
867
- }
868
-
869
- const editorComponent = this.#queryEditorElement();
870
-
871
- if (editorComponent) {
872
- editorComponent.editables[newValue] = editorComponent.editables[oldValue];
873
- delete editorComponent.editables[oldValue];
874
- }
875
- } else {
876
- this.editableElement.setAttribute(name, newValue);
877
- }
878
- }
879
-
880
- /**
881
- * Lifecycle callback when element is removed
882
- * Un-registers this editable from the parent editor
883
- */
884
- disconnectedCallback() {
885
- const editorComponent = this.#queryEditorElement();
886
-
887
- if (editorComponent) {
888
- delete editorComponent.editables[this.name];
889
- }
890
- }
891
-
892
- /**
893
- * Finds the parent editor component
894
- *
895
- * @private
896
- * @returns {CKEditorComponent|null} Parent editor component or null if not found
897
- */
898
- #queryEditorElement() {
899
- return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
900
- }
901
- }
902
-
903
- /**
904
- * Custom HTML element that represents a CKEditor UI part component.
905
- * It helpers with management of toolbar and other elements.
906
- *
907
- * @extends HTMLElement
908
- * @customElement ckeditor-ui-part-component
909
- * @example
910
- * <ckeditor-ui-part-component></ckeditor-ui-part-component>
911
- */
912
- class CKEditorUIPartComponent extends HTMLElement {
913
- /**
914
- * Lifecycle callback when element is added to DOM
915
- * Adds the toolbar to the editor UI
916
- */
917
- connectedCallback() {
918
- execIfDOMReady(async () => {
919
- const uiPart = this.getAttribute('name');
920
- const editor = await this.#queryEditorElement().instancePromise.promise;
921
-
922
- this.appendChild(editor.ui.view[uiPart].element);
923
- });
924
- }
925
-
926
- /**
927
- * Finds the parent editor component
928
- *
929
- * @private
930
- * @returns {CKEditorComponent|null} Parent editor component or null if not found
931
- */
932
- #queryEditorElement() {
933
- return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
934
- }
935
- }
936
-
937
- /**
938
- * Executes callback when DOM is ready
939
- *
940
- * @param {() => void} callback - Function to execute
941
- */
942
- function execIfDOMReady(callback) {
943
- switch (document.readyState) {
944
- case 'loading':
945
- document.addEventListener('DOMContentLoaded', callback, { once: true });
946
- break;
947
-
948
- case 'interactive':
949
- case 'complete':
950
- setTimeout(callback, 0);
951
- break;
952
-
953
- default:
954
- console.warn('Unexpected document.readyState:', document.readyState);
955
- setTimeout(callback, 0);
956
- }
957
- }
958
-
959
- /**
960
- * Dynamically imports modules based on configuration
961
- *
962
- * @param {Array<ImportConfig>} imports - Array of import configurations
963
- * @returns {Promise<Array<any>>} Loaded modules
964
- */
965
- function loadAsyncImports(imports = []) {
966
- const loadInlinePlugin = async ({ name, code }) => {
967
- const module = await import(`data:text/javascript,${encodeURIComponent(code)}`);
968
-
969
- if (!module.default) {
970
- throw new Error(`Inline plugin "${name}" must export a default class/function!`);
971
- }
972
-
973
- return module.default;
974
- };
975
-
976
- const loadExternalPlugin = async ({ import_name, import_as, window_name }) => {
977
- if (window_name) {
978
- if (!Object.prototype.hasOwnProperty.call(window, window_name)) {
979
- throw new Error(
980
- `Plugin window['${window_name}'] not found in global scope. ` +
981
- 'Please ensure the plugin is loaded before CKEditor initialization.'
982
- );
983
- }
984
-
985
- return window[window_name];
986
- }
987
-
988
- const module = await import(import_name);
989
- const imported = module[import_as || 'default'];
990
-
991
- if (!imported) {
992
- throw new Error(`Plugin "${import_as}" not found in the ESM module "${import_name}"!`);
993
- }
994
-
995
- return imported;
996
- };
997
-
998
- return Promise.all(imports.map(item => {
999
- switch(item.type) {
1000
- case 'inline':
1001
- return loadInlinePlugin(item);
1002
-
1003
- case 'external':
1004
- default:
1005
- return loadExternalPlugin(item);
1006
- }
1007
- }));
1008
- }
1009
-
1010
-
1011
- /**
1012
- * Checks if a key is safe to use in configuration objects to prevent prototype pollution.
1013
- *
1014
- * @param {string} key - Key name to check
1015
- * @returns {boolean} True if key is safe to use.
1016
- */
1017
- function isSafeKey(key) {
1018
- return typeof key === 'string' &&
1019
- key !== '__proto__' &&
1020
- key !== 'constructor' &&
1021
- key !== 'prototype';
1022
- }
1023
-
1024
- /**
1025
- * Resolves element references in configuration object.
1026
- * Looks for objects with { $element: "selector" } format and replaces them with actual elements.
1027
- *
1028
- * @param {Object} obj - Configuration object to process
1029
- * @returns {Object} Processed configuration object with resolved element references
1030
- */
1031
- function resolveElementReferences(obj) {
1032
- if (!obj || typeof obj !== 'object') {
1033
- return obj;
1034
- }
1035
-
1036
- if (Array.isArray(obj)) {
1037
- return obj.map(item => resolveElementReferences(item));
1038
- }
1039
-
1040
- const result = Object.create(null);
1041
-
1042
- for (const key of Object.getOwnPropertyNames(obj)) {
1043
- if (!isSafeKey(key)) {
1044
- console.warn(`Suspicious key "${key}" detected in config, skipping`);
1045
- continue;
1046
- }
1047
-
1048
- const value = obj[key];
1049
-
1050
- if (value && typeof value === 'object') {
1051
- if (value.$element) {
1052
- const selector = value.$element;
1053
-
1054
- if (typeof selector !== 'string') {
1055
- console.warn(`Invalid selector type for "${key}", expected string`);
1056
- continue;
1057
- }
1058
-
1059
- const element = document.querySelector(selector);
1060
-
1061
- if (!element) {
1062
- console.warn(`Element not found for selector: ${selector}`);
1063
- }
1064
-
1065
- result[key] = element || null;
1066
- } else {
1067
- result[key] = resolveElementReferences(value);
1068
- }
1069
- } else {
1070
- result[key] = value;
1071
- }
1072
- }
1073
-
1074
- return result;
1075
- }
1076
-
1077
- /**
1078
- * Custom element that provides shared CKEditor context for multiple editors.
1079
- *
1080
- * @returns {String} unique id
1081
- */
1082
- function uid() {
1083
- return Math.random().toString(36).substring(2);
1084
- }
1085
-
1086
714
  customElements.define('ckeditor-component', CKEditorComponent);
1087
- customElements.define('ckeditor-editable-component', CKEditorEditableComponent);
1088
- customElements.define('ckeditor-ui-part-component', CKEditorUIPartComponent);
1089
- customElements.define('ckeditor-context-component', CKEditorContextComponent);
@@ -0,0 +1,26 @@
1
+ class CKEditorUIPartComponent extends HTMLElement {
2
+ /**
3
+ * Lifecycle callback when element is added to DOM
4
+ * Adds the toolbar to the editor UI
5
+ */
6
+ connectedCallback() {
7
+ execIfDOMReady(async () => {
8
+ const uiPart = this.getAttribute('name');
9
+ const editor = await this.#queryEditorElement().instancePromise.promise;
10
+
11
+ this.appendChild(editor.ui.view[uiPart].element);
12
+ });
13
+ }
14
+
15
+ /**
16
+ * Finds the parent editor component
17
+ *
18
+ * @private
19
+ * @returns {CKEditorComponent|null} Parent editor component or null if not found
20
+ */
21
+ #queryEditorElement() {
22
+ return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
23
+ }
24
+ }
25
+
26
+ customElements.define('ckeditor-ui-part-component', CKEditorUIPartComponent);
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Executes callback when DOM is ready
3
+ *
4
+ * @param {() => void} callback - Function to execute when DOM is ready
5
+ */
6
+ function execIfDOMReady(callback) {
7
+ switch (document.readyState) {
8
+ case 'loading':
9
+ document.addEventListener('DOMContentLoaded', callback, { once: true });
10
+ break;
11
+
12
+ case 'interactive':
13
+ case 'complete':
14
+ setTimeout(callback, 0);
15
+ break;
16
+
17
+ default:
18
+ console.warn('Unexpected document.readyState:', document.readyState);
19
+ setTimeout(callback, 0);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Dynamically imports modules based on configuration
25
+ *
26
+ * @param {Array<Object>} imports - Array of import configurations
27
+ * @param {Object} imports[].name - Name of inline plugin (for inline type)
28
+ * @param {Object} imports[].code - Source code of inline plugin (for inline type)
29
+ * @param {Object} imports[].import_name - Module path to import (for external type)
30
+ * @param {Object} imports[].import_as - Name to import as (for external type)
31
+ * @param {Object} imports[].window_name - Global window object name (for external type)
32
+ * @param {('inline'|'external')} imports[].type - Type of import
33
+ * @returns {Promise<Array<any>>} Array of loaded modules
34
+ * @throws {Error} When plugin loading fails
35
+ */
36
+ function loadAsyncImports(imports = []) {
37
+ const loadInlinePlugin = async ({ name, code }) => {
38
+ const module = await import(`data:text/javascript,${encodeURIComponent(code)}`);
39
+
40
+ if (!module.default) {
41
+ throw new Error(`Inline plugin "${name}" must export a default class/function!`);
42
+ }
43
+
44
+ return module.default;
45
+ };
46
+
47
+ const loadExternalPlugin = async ({ import_name, import_as, window_name }) => {
48
+ if (window_name) {
49
+ if (!Object.prototype.hasOwnProperty.call(window, window_name)) {
50
+ throw new Error(
51
+ `Plugin window['${window_name}'] not found in global scope. ` +
52
+ 'Please ensure the plugin is loaded before CKEditor initialization.'
53
+ );
54
+ }
55
+
56
+ return window[window_name];
57
+ }
58
+
59
+ const module = await import(import_name);
60
+ const imported = module[import_as || 'default'];
61
+
62
+ if (!imported) {
63
+ throw new Error(`Plugin "${import_as}" not found in the ESM module "${import_name}"!`);
64
+ }
65
+
66
+ return imported;
67
+ };
68
+
69
+ return Promise.all(imports.map(item => {
70
+ switch(item.type) {
71
+ case 'inline':
72
+ return loadInlinePlugin(item);
73
+
74
+ case 'external':
75
+ default:
76
+ return loadExternalPlugin(item);
77
+ }
78
+ }));
79
+ }
80
+
81
+ /**
82
+ * Checks if a key is safe to use in configuration objects to prevent prototype pollution
83
+ *
84
+ * @param {string} key - Key name to check
85
+ * @returns {boolean} True if key is safe to use
86
+ */
87
+ function isSafeKey(key) {
88
+ return typeof key === 'string' &&
89
+ key !== '__proto__' &&
90
+ key !== 'constructor' &&
91
+ key !== 'prototype';
92
+ }
93
+
94
+ /**
95
+ * Resolves element references in configuration object.
96
+ * Looks for objects with { $element: "selector" } format and replaces them with actual DOM elements.
97
+ *
98
+ * @param {Object} obj - Configuration object to process
99
+ * @returns {Object} Processed configuration object with resolved element references
100
+ * @throws {Error} When element reference is invalid
101
+ */
102
+ function resolveElementReferences(obj) {
103
+ if (!obj || typeof obj !== 'object') {
104
+ return obj;
105
+ }
106
+
107
+ if (Array.isArray(obj)) {
108
+ return obj.map(item => resolveElementReferences(item));
109
+ }
110
+
111
+ const result = Object.create(null);
112
+
113
+ for (const key of Object.getOwnPropertyNames(obj)) {
114
+ if (!isSafeKey(key)) {
115
+ console.warn(`Suspicious key "${key}" detected in config, skipping`);
116
+ continue;
117
+ }
118
+
119
+ const value = obj[key];
120
+
121
+ if (value && typeof value === 'object') {
122
+ if (value.$element) {
123
+ const selector = value.$element;
124
+
125
+ if (typeof selector !== 'string') {
126
+ console.warn(`Invalid selector type for "${key}", expected string`);
127
+ continue;
128
+ }
129
+
130
+ const element = document.querySelector(selector);
131
+
132
+ if (!element) {
133
+ console.warn(`Element not found for selector: ${selector}`);
134
+ }
135
+
136
+ result[key] = element || null;
137
+ } else {
138
+ result[key] = resolveElementReferences(value);
139
+ }
140
+ } else {
141
+ result[key] = value;
142
+ }
143
+ }
144
+
145
+ return result;
146
+ }
147
+
148
+ /**
149
+ * Generates a unique identifier string
150
+ *
151
+ * @returns {string} Random string that can be used as unique identifier
152
+ */
153
+ function uid() {
154
+ return Math.random().toString(36).substring(2);
155
+ }
@@ -6,7 +6,7 @@ require_relative 'ckbox_bundle'
6
6
 
7
7
  module CKEditor5::Rails
8
8
  module Cdn::Helpers
9
- def ckeditor5_cdn_assets(preset: :default, **kwargs)
9
+ def ckeditor5_assets(preset: :default, **kwargs)
10
10
  merge_with_editor_preset(preset, **kwargs) => {
11
11
  cdn:,
12
12
  version:,
@@ -32,15 +32,7 @@ module CKEditor5::Rails
32
32
 
33
33
  Cdn::UrlGenerator::CDN_THIRD_PARTY_GENERATORS.each_key do |key|
34
34
  define_method(:"ckeditor5_#{key.to_s.parameterize}_assets") do |**kwargs|
35
- ckeditor5_cdn_assets(**kwargs.merge(cdn: key))
36
- end
37
- end
38
-
39
- def ckeditor5_assets(**kwargs)
40
- if kwargs[:license_key] && kwargs[:license_key] != 'GPL'
41
- ckeditor5_cloud_assets(**kwargs)
42
- else
43
- ckeditor5_cdn_assets(**kwargs.merge(cdn: Engine.default_preset.cdn))
35
+ ckeditor5_assets(**kwargs.merge(cdn: key))
44
36
  end
45
37
  end
46
38
 
@@ -62,7 +54,7 @@ module CKEditor5::Rails
62
54
 
63
55
  raise ArgumentError,
64
56
  "Poor thing. You forgot to define #{key}. Make sure you passed `#{key}:` parameter to " \
65
- "`ckeditor5_cdn_assets` or defined default one in your `#{preset}` preset!"
57
+ "`ckeditor5_assets` or defined default one in your `#{preset}` preset!"
66
58
  end
67
59
 
68
60
  hash
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'cdn/helpers'
4
- require_relative 'cloud/helpers'
5
4
  require_relative 'editor/helpers'
6
5
  require_relative 'context/helpers'
7
6
 
8
7
  module CKEditor5::Rails
9
8
  module Helpers
10
9
  include Cdn::Helpers
11
- include Cloud::Helpers
12
10
  include Editor::Helpers
13
11
  include Context::Helpers
14
12
  end
@@ -22,6 +22,18 @@ module CKEditor5::Rails
22
22
  }
23
23
  end
24
24
 
25
+ def premium?
26
+ @premium
27
+ end
28
+
29
+ def gpl?
30
+ license_key == 'GPL'
31
+ end
32
+
33
+ def menubar?
34
+ @config.dig(:menuBar, :isVisible) || false
35
+ end
36
+
25
37
  def to_h_with_overrides(**overrides)
26
38
  {
27
39
  version: overrides.fetch(:version, version),
@@ -51,6 +63,8 @@ module CKEditor5::Rails
51
63
  return @license_key if license_key.nil?
52
64
 
53
65
  @license_key = license_key
66
+
67
+ cdn(:cloud) unless gpl?
54
68
  end
55
69
 
56
70
  def gpl
@@ -134,7 +148,9 @@ module CKEditor5::Rails
134
148
  names.each { |name| plugin(name, **kwargs) }
135
149
  end
136
150
 
137
- def language(ui, content: ui) # rubocop:disable Naming/MethodParameterName
151
+ def language(ui = nil, content: ui) # rubocop:disable Naming/MethodParameterName
152
+ return @config[:language] if ui.nil?
153
+
138
154
  @config[:language] = {
139
155
  ui: ui,
140
156
  content: content
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.9.0'
5
+ VERSION = '1.10.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-14 00:00:00.000000000 Z
12
+ date: 2024-11-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -45,12 +45,16 @@ files:
45
45
  - lib/ckeditor5/rails.rb
46
46
  - lib/ckeditor5/rails/assets/assets_bundle.rb
47
47
  - lib/ckeditor5/rails/assets/assets_bundle_html_serializer.rb
48
- - lib/ckeditor5/rails/assets/webcomponent.mjs
48
+ - lib/ckeditor5/rails/assets/webcomponent_bundle.rb
49
+ - lib/ckeditor5/rails/assets/webcomponents/components/context.mjs
50
+ - lib/ckeditor5/rails/assets/webcomponents/components/editable.mjs
51
+ - lib/ckeditor5/rails/assets/webcomponents/components/editor.mjs
52
+ - lib/ckeditor5/rails/assets/webcomponents/components/ui-part.mjs
53
+ - lib/ckeditor5/rails/assets/webcomponents/utils.mjs
49
54
  - lib/ckeditor5/rails/cdn/ckbox_bundle.rb
50
55
  - lib/ckeditor5/rails/cdn/ckeditor_bundle.rb
51
56
  - lib/ckeditor5/rails/cdn/helpers.rb
52
57
  - lib/ckeditor5/rails/cdn/url_generator.rb
53
- - lib/ckeditor5/rails/cloud/helpers.rb
54
58
  - lib/ckeditor5/rails/context/helpers.rb
55
59
  - lib/ckeditor5/rails/context/props.rb
56
60
  - lib/ckeditor5/rails/editor/config_helpers.rb
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CKEditor5::Rails
4
- module Cloud
5
- module Helpers
6
- def ckeditor5_cloud_assets(license_key:, **kwargs)
7
- raise 'Cloud assets are not permitted in GPL license!' if license_key == 'GPL'
8
-
9
- ckeditor5_cdn_assets(cdn: :cloud, license_key: license_key, **kwargs)
10
- end
11
- end
12
- end
13
- end