ckeditor5 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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