ckeditor5 1.35.1 → 1.82.3
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.
Potentially problematic release.
This version of ckeditor5 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +25 -158
- data/lib/ckeditor5/rails/assets/assets_bundle_html_serializer.rb +2 -2
- data/lib/ckeditor5/rails/assets/webcomponent_bundle.rb +21 -2
- data/lib/ckeditor5/rails/assets/webcomponents/components/context.mjs +123 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/editable.mjs +113 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/editor.mjs +778 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/ui-part.mjs +26 -0
- data/lib/ckeditor5/rails/assets/webcomponents/utils.mjs +235 -0
- data/lib/ckeditor5/rails/cdn/ckeditor_bundle.rb +2 -2
- data/lib/ckeditor5/rails/cdn/concerns/bundle_builder.rb +7 -5
- data/lib/ckeditor5/rails/cdn/helpers.rb +2 -2
- data/lib/ckeditor5/rails/context/helpers.rb +1 -6
- data/lib/ckeditor5/rails/context/preset_builder.rb +2 -2
- data/lib/ckeditor5/rails/editor/helpers/config_helpers.rb +1 -1
- data/lib/ckeditor5/rails/editor/helpers/editor_helpers.rb +4 -14
- data/lib/ckeditor5/rails/editor/props.rb +12 -7
- data/lib/ckeditor5/rails/editor/props_inline_plugin.rb +3 -6
- data/lib/ckeditor5/rails/editor/props_patch_plugin.rb +4 -2
- data/lib/ckeditor5/rails/engine.rb +6 -7
- data/lib/ckeditor5/rails/plugins/custom_translations_loader.rb +4 -3
- data/lib/ckeditor5/rails/plugins/simple_upload_adapter.rb +3 -2
- data/lib/ckeditor5/rails/plugins/special_characters_bootstrap.rb +3 -2
- data/lib/ckeditor5/rails/plugins/wproofreader.rb +3 -2
- data/lib/ckeditor5/rails/presets/concerns/plugin_methods.rb +3 -39
- data/lib/ckeditor5/rails/presets/manager.rb +3 -8
- data/lib/ckeditor5/rails/presets/preset_builder.rb +18 -28
- data/lib/ckeditor5/rails/presets/special_characters_builder.rb +2 -1
- data/lib/ckeditor5/rails/semver.rb +1 -1
- data/lib/ckeditor5/rails/version.rb +2 -2
- data/spec/e2e/features/context_spec.rb +1 -1
- data/spec/e2e/spec_helper.rb +1 -1
- data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +12 -15
- data/spec/lib/ckeditor5/rails/context/preset_builder_spec.rb +0 -18
- data/spec/lib/ckeditor5/rails/editor/helpers/editor_helpers_spec.rb +0 -27
- data/spec/lib/ckeditor5/rails/editor/props_inline_plugin_spec.rb +0 -28
- data/spec/lib/ckeditor5/rails/editor/props_spec.rb +3 -13
- data/spec/lib/ckeditor5/rails/presets/manager_spec.rb +2 -58
- data/spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb +2 -26
- data/spec/spec_helper.rb +1 -1
- metadata +10 -47
- data/lib/ckeditor5/rails/plugins/patches/fix_color_picker_race_condition.rb +0 -50
- data/lib/ckeditor5/rails/plugins.rb +0 -15
- data/npm_package/dist/index.cjs +0 -2
- data/npm_package/dist/index.cjs.map +0 -1
- data/npm_package/dist/index.d.ts +0 -1
- data/npm_package/dist/index.mjs +0 -723
- data/npm_package/dist/index.mjs.map +0 -1
- data/npm_package/dist/src/components/context.d.ts +0 -24
- data/npm_package/dist/src/components/context.d.ts.map +0 -1
- data/npm_package/dist/src/components/editable.d.ts +0 -34
- data/npm_package/dist/src/components/editable.d.ts.map +0 -1
- data/npm_package/dist/src/components/editor/editor.d.ts +0 -79
- data/npm_package/dist/src/components/editor/editor.d.ts.map +0 -1
- data/npm_package/dist/src/components/editor/index.d.ts +0 -3
- data/npm_package/dist/src/components/editor/index.d.ts.map +0 -1
- data/npm_package/dist/src/components/editor/multiroot-editables-tracker.d.ts +0 -36
- data/npm_package/dist/src/components/editor/multiroot-editables-tracker.d.ts.map +0 -1
- data/npm_package/dist/src/components/index.d.ts +0 -5
- data/npm_package/dist/src/components/index.d.ts.map +0 -1
- data/npm_package/dist/src/components/ui-part.d.ts +0 -2
- data/npm_package/dist/src/components/ui-part.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/exec-if-dom-ready.d.ts +0 -7
- data/npm_package/dist/src/helpers/exec-if-dom-ready.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/index.d.ts +0 -8
- data/npm_package/dist/src/helpers/index.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/inject-script.d.ts +0 -8
- data/npm_package/dist/src/helpers/inject-script.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/is-safe-key.d.ts +0 -8
- data/npm_package/dist/src/helpers/is-safe-key.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/load-async-css.d.ts +0 -9
- data/npm_package/dist/src/helpers/load-async-css.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/load-async-imports.d.ts +0 -25
- data/npm_package/dist/src/helpers/load-async-imports.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/resolve-config-element-references.d.ts +0 -9
- data/npm_package/dist/src/helpers/resolve-config-element-references.d.ts.map +0 -1
- data/npm_package/dist/src/helpers/uid.d.ts +0 -7
- data/npm_package/dist/src/helpers/uid.d.ts.map +0 -1
- data/npm_package/dist/src/index.d.ts +0 -1
- data/npm_package/dist/src/index.d.ts.map +0 -1
- data/npm_package/dist/vite.config.d.ts +0 -3
- data/npm_package/dist/vite.config.d.ts.map +0 -1
- data/npm_package/package.json +0 -41
|
@@ -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,235 @@
|
|
|
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 loadExternalPlugin = async ({ url, import_name, import_as, window_name, stylesheets }) => {
|
|
38
|
+
if (stylesheets?.length) {
|
|
39
|
+
await loadAsyncCSS(stylesheets);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (window_name) {
|
|
43
|
+
function isScriptPresent() {
|
|
44
|
+
return Object.prototype.hasOwnProperty.call(window, window_name);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (url && !isScriptPresent()) {
|
|
48
|
+
await injectScript(url);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!isScriptPresent()) {
|
|
52
|
+
window.dispatchEvent(
|
|
53
|
+
new CustomEvent(`ckeditor:request-cjs-plugin:${window_name}`)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!isScriptPresent()) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Plugin window['${window_name}'] not found in global scope. ` +
|
|
60
|
+
'Please ensure the plugin is loaded before CKEditor initialization.'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return window[window_name];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const module = await import(import_name);
|
|
68
|
+
const imported = module[import_as || 'default'];
|
|
69
|
+
|
|
70
|
+
if (!imported) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Plugin "${import_as || 'default'}" not found in the ESM module ` +
|
|
73
|
+
`"${import_name}"! Available imports: ${Object.keys(module).join(', ')}! ` +
|
|
74
|
+
'Consider changing "import_as" value.'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return imported;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function uncompressImport(pkg) {
|
|
82
|
+
if (typeof pkg === 'string') {
|
|
83
|
+
return loadExternalPlugin({ import_name: 'ckeditor5', import_as: pkg });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return loadExternalPlugin(pkg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Promise.all(imports.map(uncompressImport));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Checks if stylesheet with given href already exists in document
|
|
94
|
+
*
|
|
95
|
+
* @param {string} href - Stylesheet URL to check
|
|
96
|
+
* @returns {boolean} True if stylesheet already exists
|
|
97
|
+
*/
|
|
98
|
+
function stylesheetExists(href) {
|
|
99
|
+
return Array
|
|
100
|
+
.from(document.styleSheets)
|
|
101
|
+
.some(sheet =>
|
|
102
|
+
sheet.href === href || sheet.href === new URL(href, window.location.href).href
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Dynamically loads CSS files based on configuration
|
|
108
|
+
*
|
|
109
|
+
* @param {Array<string>} imports - Array of CSS file URLs to load
|
|
110
|
+
* @returns {Promise<Array<void>>} Array of promises for each CSS file load
|
|
111
|
+
* @throws {Error} When CSS file loading fails
|
|
112
|
+
*/
|
|
113
|
+
function loadAsyncCSS(stylesheets = []) {
|
|
114
|
+
const promises = stylesheets.map(href =>
|
|
115
|
+
new Promise((resolve, reject) => {
|
|
116
|
+
if (stylesheetExists(href)) {
|
|
117
|
+
resolve();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const link = document.createElement('link');
|
|
122
|
+
link.rel = 'stylesheet';
|
|
123
|
+
link.href = href;
|
|
124
|
+
|
|
125
|
+
link.onerror = reject;
|
|
126
|
+
link.onload = () => resolve();
|
|
127
|
+
|
|
128
|
+
document.head.appendChild(link);
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return Promise.all(promises);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const SCRIPT_LOAD_PROMISES = new Map();
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Dynamically loads script files based on configuration.
|
|
139
|
+
* Uses caching to avoid loading the same script multiple times.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} url - URL of the script to load
|
|
142
|
+
*/
|
|
143
|
+
function injectScript(url) {
|
|
144
|
+
if (SCRIPT_LOAD_PROMISES.has(url)) {
|
|
145
|
+
return SCRIPT_LOAD_PROMISES.get(url);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const promise = new Promise((resolve, reject) => {
|
|
149
|
+
const script = document.createElement('script');
|
|
150
|
+
script.src = url;
|
|
151
|
+
script.onload = resolve;
|
|
152
|
+
script.onerror = reject;
|
|
153
|
+
|
|
154
|
+
document.head.appendChild(script);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
SCRIPT_LOAD_PROMISES.set(url, promise);
|
|
158
|
+
return promise;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Checks if a key is safe to use in configuration objects to prevent prototype pollution
|
|
163
|
+
*
|
|
164
|
+
* @param {string} key - Key name to check
|
|
165
|
+
* @returns {boolean} True if key is safe to use
|
|
166
|
+
*/
|
|
167
|
+
function isSafeKey(key) {
|
|
168
|
+
return typeof key === 'string' &&
|
|
169
|
+
key !== '__proto__' &&
|
|
170
|
+
key !== 'constructor' &&
|
|
171
|
+
key !== 'prototype';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolves element references in configuration object.
|
|
176
|
+
* Looks for objects with { $element: "selector" } format and replaces them with actual DOM elements.
|
|
177
|
+
*
|
|
178
|
+
* @param {Object} obj - Configuration object to process
|
|
179
|
+
* @returns {Object} Processed configuration object with resolved element references
|
|
180
|
+
* @throws {Error} When element reference is invalid
|
|
181
|
+
*/
|
|
182
|
+
function resolveElementReferences(obj) {
|
|
183
|
+
if (!obj || typeof obj !== 'object') {
|
|
184
|
+
return obj;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Array.isArray(obj)) {
|
|
188
|
+
return obj.map(item => resolveElementReferences(item));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const result = Object.create(null);
|
|
192
|
+
|
|
193
|
+
for (const key of Object.getOwnPropertyNames(obj)) {
|
|
194
|
+
if (!isSafeKey(key)) {
|
|
195
|
+
console.warn(`Suspicious key "${key}" detected in config, skipping`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const value = obj[key];
|
|
200
|
+
|
|
201
|
+
if (value && typeof value === 'object') {
|
|
202
|
+
if (value.$element) {
|
|
203
|
+
const selector = value.$element;
|
|
204
|
+
|
|
205
|
+
if (typeof selector !== 'string') {
|
|
206
|
+
console.warn(`Invalid selector type for "${key}", expected string`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const element = document.querySelector(selector);
|
|
211
|
+
|
|
212
|
+
if (!element) {
|
|
213
|
+
console.warn(`Element not found for selector: ${selector}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
result[key] = element || null;
|
|
217
|
+
} else {
|
|
218
|
+
result[key] = resolveElementReferences(value);
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
result[key] = value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Generates a unique identifier string
|
|
230
|
+
*
|
|
231
|
+
* @returns {string} Random string that can be used as unique identifier
|
|
232
|
+
*/
|
|
233
|
+
function uid() {
|
|
234
|
+
return Math.random().toString(36).substring(2);
|
|
235
|
+
}
|
|
@@ -43,7 +43,7 @@ module CKEditor5::Rails
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def translations_js_url_imports
|
|
46
|
-
translations.
|
|
46
|
+
translations.filter_map do |lang|
|
|
47
47
|
next if lang == :en
|
|
48
48
|
|
|
49
49
|
url = create_cdn_url(import_name, version, "translations/#{lang}.js")
|
|
@@ -53,7 +53,7 @@ module CKEditor5::Rails
|
|
|
53
53
|
import_name: "#{import_name}/translations/#{lang}.js",
|
|
54
54
|
translation: true
|
|
55
55
|
)
|
|
56
|
-
end
|
|
56
|
+
end
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
end
|
|
@@ -4,11 +4,13 @@ module CKEditor5::Rails
|
|
|
4
4
|
module Cdn::Concerns
|
|
5
5
|
module BundleBuilder
|
|
6
6
|
def create_preset_bundle(preset)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
preset => {
|
|
8
|
+
cdn:,
|
|
9
|
+
version:,
|
|
10
|
+
translations:,
|
|
11
|
+
ckbox:,
|
|
12
|
+
premium:
|
|
13
|
+
}
|
|
12
14
|
|
|
13
15
|
bundle = build_base_cdn_bundle(cdn, version, translations)
|
|
14
16
|
bundle << build_premium_cdn_bundle(cdn, version, translations) if premium
|
|
@@ -55,7 +55,7 @@ module CKEditor5::Rails
|
|
|
55
55
|
#
|
|
56
56
|
# @example Using preset builder object
|
|
57
57
|
# <% @preset = ckeditor5_preset do
|
|
58
|
-
# version '
|
|
58
|
+
# version '44.3.0'
|
|
59
59
|
# toolbar :bold, :italic
|
|
60
60
|
# plugins :Bold, :Italic
|
|
61
61
|
# end %>
|
|
@@ -169,7 +169,7 @@ module CKEditor5::Rails
|
|
|
169
169
|
if language.present?
|
|
170
170
|
new_preset.language(language)
|
|
171
171
|
elsif !new_preset.language?
|
|
172
|
-
new_preset.language(I18n.locale
|
|
172
|
+
new_preset.language(I18n.locale)
|
|
173
173
|
end
|
|
174
174
|
|
|
175
175
|
validate_required_preset_params!(new_preset, preset)
|
|
@@ -31,12 +31,7 @@ module CKEditor5::Rails::Context
|
|
|
31
31
|
|
|
32
32
|
tags = []
|
|
33
33
|
tags << ckeditor5_inline_plugins_tags(preset)
|
|
34
|
-
tags << tag.public_send(
|
|
35
|
-
:'ckeditor-context-component',
|
|
36
|
-
'data-turbo-temporary': true,
|
|
37
|
-
**context_props.to_attributes,
|
|
38
|
-
&block
|
|
39
|
-
)
|
|
34
|
+
tags << tag.public_send(:'ckeditor-context-component', **context_props.to_attributes, &block)
|
|
40
35
|
|
|
41
36
|
safe_join(tags)
|
|
42
37
|
end
|
|
@@ -12,7 +12,7 @@ module CKEditor5::Rails
|
|
|
12
12
|
#
|
|
13
13
|
# @example Basic preset definition
|
|
14
14
|
# preset = PresetBuilder.new do
|
|
15
|
-
# version '
|
|
15
|
+
# version '44.3.0'
|
|
16
16
|
# gpl
|
|
17
17
|
# type :classic
|
|
18
18
|
# toolbar :bold, :italic
|
|
@@ -35,7 +35,7 @@ module CKEditor5::Rails
|
|
|
35
35
|
# @param block [Proc] Optional configuration block
|
|
36
36
|
# @example Initialize with block
|
|
37
37
|
# PresetBuilder.new do
|
|
38
|
-
# version '
|
|
38
|
+
# version '44.3.0'
|
|
39
39
|
# toolbar :bold, :italic
|
|
40
40
|
# end
|
|
41
41
|
def initialize(&block)
|
|
@@ -44,7 +44,7 @@ module CKEditor5::Rails::Editor::Helpers
|
|
|
44
44
|
#
|
|
45
45
|
# @example Creating a custom preset in controller
|
|
46
46
|
# @preset = ckeditor5_preset do
|
|
47
|
-
# version '
|
|
47
|
+
# version '44.3.0'
|
|
48
48
|
# toolbar :sourceEditing, :|, :bold, :italic
|
|
49
49
|
# plugins :Essentials, :Paragraph, :Bold, :Italic
|
|
50
50
|
# end
|
|
@@ -74,7 +74,7 @@ module CKEditor5::Rails
|
|
|
74
74
|
|
|
75
75
|
# Add some fallbacks
|
|
76
76
|
config[:licenseKey] ||= context[:license_key] || preset.license_key
|
|
77
|
-
config[:language] = { ui: language
|
|
77
|
+
config[:language] = { ui: language } if language
|
|
78
78
|
|
|
79
79
|
editor_props = Editor::Props.new(
|
|
80
80
|
type, config,
|
|
@@ -85,7 +85,7 @@ module CKEditor5::Rails
|
|
|
85
85
|
|
|
86
86
|
tag_attributes = html_attributes.merge(editor_props.to_attributes)
|
|
87
87
|
|
|
88
|
-
tag.public_send(:'ckeditor-component',
|
|
88
|
+
tag.public_send(:'ckeditor-component', **tag_attributes, &block)
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# Creates an editable area for multiroot or decoupled editors.
|
|
@@ -96,12 +96,7 @@ module CKEditor5::Rails
|
|
|
96
96
|
# @example Creating a named editable area in multiroot editor
|
|
97
97
|
# <%= ckeditor5_editable 'content', style: 'border: 1px solid gray' %>
|
|
98
98
|
def ckeditor5_editable(name = nil, **kwargs, &block)
|
|
99
|
-
tag.public_send(
|
|
100
|
-
:'ckeditor-editable-component',
|
|
101
|
-
'data-turbo-temporary': true,
|
|
102
|
-
name: name,
|
|
103
|
-
**kwargs, &block
|
|
104
|
-
)
|
|
99
|
+
tag.public_send(:'ckeditor-editable-component', name: name, **kwargs, &block)
|
|
105
100
|
end
|
|
106
101
|
|
|
107
102
|
# Creates a UI part component for the editor (toolbar, menubar).
|
|
@@ -112,12 +107,7 @@ module CKEditor5::Rails
|
|
|
112
107
|
# @example Creating a toolbar component
|
|
113
108
|
# <%= ckeditor5_ui_part 'toolbar' %>
|
|
114
109
|
def ckeditor5_ui_part(name, **kwargs, &block)
|
|
115
|
-
tag.public_send(
|
|
116
|
-
:'ckeditor-ui-part-component',
|
|
117
|
-
'data-turbo-temporary': true,
|
|
118
|
-
name: name,
|
|
119
|
-
**kwargs, &block
|
|
120
|
-
)
|
|
110
|
+
tag.public_send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
|
|
121
111
|
end
|
|
122
112
|
|
|
123
113
|
# Creates a toolbar component for decoupled editor.
|
|
@@ -30,14 +30,9 @@ module CKEditor5::Rails::Editor
|
|
|
30
30
|
|
|
31
31
|
def to_attributes
|
|
32
32
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
config: serialize_config,
|
|
36
|
-
watchdog: watchdog,
|
|
37
|
-
type: EDITOR_TYPES[@type]
|
|
33
|
+
type: EDITOR_TYPES[@type],
|
|
34
|
+
**serialized_attributes
|
|
38
35
|
}
|
|
39
|
-
.merge(editable_height ? { 'editable-height': editable_height } : {})
|
|
40
|
-
.transform_keys(&:to_sym)
|
|
41
36
|
end
|
|
42
37
|
|
|
43
38
|
def self.valid_editor_type?(type)
|
|
@@ -48,6 +43,16 @@ module CKEditor5::Rails::Editor
|
|
|
48
43
|
|
|
49
44
|
attr_reader :bundle, :watchdog, :type, :config, :editable_height
|
|
50
45
|
|
|
46
|
+
def serialized_attributes
|
|
47
|
+
{
|
|
48
|
+
bundle: bundle.to_json,
|
|
49
|
+
plugins: serialize_plugins,
|
|
50
|
+
config: serialize_config,
|
|
51
|
+
watchdog: watchdog
|
|
52
|
+
}
|
|
53
|
+
.merge(editable_height ? { 'editable-height' => editable_height } : {})
|
|
54
|
+
end
|
|
55
|
+
|
|
51
56
|
def serialize_plugins
|
|
52
57
|
(config[:plugins] || []).map { |plugin| PropsBasePlugin.normalize(plugin).to_h }.to_json
|
|
53
58
|
end
|
|
@@ -4,20 +4,17 @@ require_relative 'props_base_plugin'
|
|
|
4
4
|
|
|
5
5
|
module CKEditor5::Rails::Editor
|
|
6
6
|
class PropsInlinePlugin < PropsBasePlugin
|
|
7
|
-
attr_reader :code
|
|
7
|
+
attr_reader :code
|
|
8
8
|
|
|
9
|
-
def initialize(name, code
|
|
9
|
+
def initialize(name, code)
|
|
10
10
|
super(name)
|
|
11
11
|
|
|
12
12
|
raise ArgumentError, 'Code must be a String' unless code.is_a?(String)
|
|
13
13
|
|
|
14
14
|
@code = "(async () => { #{code} })()"
|
|
15
|
-
@compress = compress
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
def
|
|
19
|
-
return unless @compress
|
|
20
|
-
|
|
17
|
+
def compress!
|
|
21
18
|
@code = Terser.new(compress: false, mangle: true).compile(@code)
|
|
22
19
|
end
|
|
23
20
|
|
|
@@ -7,11 +7,13 @@ module CKEditor5::Rails::Editor
|
|
|
7
7
|
class PropsPatchPlugin < PropsInlinePlugin
|
|
8
8
|
attr_reader :min_version, :max_version
|
|
9
9
|
|
|
10
|
-
def initialize(name, code, min_version: nil, max_version: nil
|
|
11
|
-
super(name, code
|
|
10
|
+
def initialize(name, code, min_version: nil, max_version: nil)
|
|
11
|
+
super(name, code)
|
|
12
12
|
|
|
13
13
|
@min_version = min_version && CKEditor5::Rails::Semver.new(min_version)
|
|
14
14
|
@max_version = max_version && CKEditor5::Rails::Semver.new(max_version)
|
|
15
|
+
|
|
16
|
+
compress!
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def self.applicable_for_version?(editor_version, min_version: nil, max_version: nil)
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
require 'rails/engine'
|
|
4
4
|
|
|
5
5
|
require_relative 'presets/manager'
|
|
6
|
-
require_relative 'plugins'
|
|
6
|
+
require_relative 'plugins/simple_upload_adapter'
|
|
7
|
+
require_relative 'plugins/wproofreader'
|
|
8
|
+
require_relative 'plugins/special_characters_bootstrap'
|
|
9
|
+
require_relative 'plugins/custom_translations_loader'
|
|
7
10
|
|
|
8
11
|
module CKEditor5::Rails
|
|
9
12
|
class PresetNotFoundError < ArgumentError; end
|
|
@@ -54,10 +57,7 @@ module CKEditor5::Rails
|
|
|
54
57
|
|
|
55
58
|
def configure(&block)
|
|
56
59
|
proxy = ConfigurationProxy.new(config.ckeditor5)
|
|
57
|
-
|
|
58
|
-
config.after_initialize do
|
|
59
|
-
proxy.instance_eval(&block)
|
|
60
|
-
end
|
|
60
|
+
proxy.instance_eval(&block)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def find_preset(preset)
|
|
@@ -81,8 +81,7 @@ module CKEditor5::Rails
|
|
|
81
81
|
:type, :menubar, :plugins, :plugin, :inline_plugin,
|
|
82
82
|
:toolbar, :block_toolbar, :balloon_toolbar,
|
|
83
83
|
:language, :ckbox, :configure, :automatic_upgrades, :simple_upload_adapter,
|
|
84
|
-
:editable_height, :wproofreader, :custom_translations, :
|
|
85
|
-
:compression, :compression?, to: :default_preset
|
|
84
|
+
:editable_height, :wproofreader, :custom_translations, to: :default_preset
|
|
86
85
|
|
|
87
86
|
def initialize(configuration)
|
|
88
87
|
@configuration = configuration
|
|
@@ -4,8 +4,8 @@ require_relative '../editor/props_inline_plugin'
|
|
|
4
4
|
|
|
5
5
|
module CKEditor5::Rails::Plugins
|
|
6
6
|
class CustomTranslationsLoader < CKEditor5::Rails::Editor::PropsInlinePlugin
|
|
7
|
-
def initialize(translations
|
|
8
|
-
code = <<~JS
|
|
7
|
+
def initialize(translations) # rubocop:disable Metrics/MethodLength
|
|
8
|
+
code = <<~JS.freeze
|
|
9
9
|
const { Plugin } = await import('ckeditor5');
|
|
10
10
|
|
|
11
11
|
function resolveTranslationReferences(uiLanguage, config, visited = new WeakSet()) {
|
|
@@ -90,7 +90,8 @@ module CKEditor5::Rails::Plugins
|
|
|
90
90
|
}
|
|
91
91
|
JS
|
|
92
92
|
|
|
93
|
-
super(:CustomTranslationsLoader, code
|
|
93
|
+
super(:CustomTranslationsLoader, code)
|
|
94
|
+
compress!
|
|
94
95
|
end
|
|
95
96
|
end
|
|
96
97
|
end
|
|
@@ -12,30 +12,11 @@ module CKEditor5::Rails
|
|
|
12
12
|
class MissingInlinePluginError < StandardError; end
|
|
13
13
|
class UnsupportedESModuleError < StandardError; end
|
|
14
14
|
class InvalidPatchPluginError < ArgumentError; end
|
|
15
|
-
class CompressionDisabledError < StandardError; end
|
|
16
15
|
|
|
17
16
|
included do
|
|
18
17
|
attr_reader :disallow_inline_plugin_compression
|
|
19
18
|
end
|
|
20
19
|
|
|
21
|
-
# Sets compression of inline plugin code. Make sure that it is called before setting the version
|
|
22
|
-
# or adding plugins.
|
|
23
|
-
# @example Disable compression
|
|
24
|
-
# compression(enabled: false)
|
|
25
|
-
# @return [void]
|
|
26
|
-
# @note This method is useful for debugging purposes, as it allows you to see the uncompressed code.
|
|
27
|
-
def compression(enabled: false)
|
|
28
|
-
@disallow_inline_plugin_compression = !enabled
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Check if compression is enabled
|
|
32
|
-
# @return [Boolean] True if compression is enabled, false otherwise
|
|
33
|
-
# @example Check if compression is enabled
|
|
34
|
-
# compression? # => true
|
|
35
|
-
def compression?
|
|
36
|
-
!@disallow_inline_plugin_compression
|
|
37
|
-
end
|
|
38
|
-
|
|
39
20
|
# Registers an external plugin loaded from a URL
|
|
40
21
|
#
|
|
41
22
|
# @param name [Symbol] Plugin name
|
|
@@ -54,7 +35,6 @@ module CKEditor5::Rails
|
|
|
54
35
|
#
|
|
55
36
|
# @param name [Symbol] Plugin name
|
|
56
37
|
# @param code [String] JavaScript code defining the plugin
|
|
57
|
-
# @param compress [Boolean] Whether to compress the code (default: true)
|
|
58
38
|
# @example Define custom highlight plugin
|
|
59
39
|
# inline_plugin :MyCustomPlugin, <<~JS
|
|
60
40
|
# const { Plugin } = await import( 'ckeditor5' );
|
|
@@ -69,7 +49,7 @@ module CKEditor5::Rails
|
|
|
69
49
|
# }
|
|
70
50
|
# }
|
|
71
51
|
# JS
|
|
72
|
-
def inline_plugin(name, code
|
|
52
|
+
def inline_plugin(name, code)
|
|
73
53
|
if code.match?(/export default/)
|
|
74
54
|
raise UnsupportedESModuleError,
|
|
75
55
|
'Inline plugins must not use ES module syntax!' \
|
|
@@ -81,7 +61,8 @@ module CKEditor5::Rails
|
|
|
81
61
|
'Plugin code must return a class that extends Plugin!'
|
|
82
62
|
end
|
|
83
63
|
|
|
84
|
-
plugin = Editor::PropsInlinePlugin.new(name, code
|
|
64
|
+
plugin = Editor::PropsInlinePlugin.new(name, code)
|
|
65
|
+
plugin.compress! unless disallow_inline_plugin_compression
|
|
85
66
|
|
|
86
67
|
register_plugin(plugin)
|
|
87
68
|
end
|
|
@@ -140,23 +121,6 @@ module CKEditor5::Rails
|
|
|
140
121
|
builder
|
|
141
122
|
end
|
|
142
123
|
|
|
143
|
-
# Compresses inline plugins to reduce bundle size
|
|
144
|
-
#
|
|
145
|
-
# @raise [CompressionDisabledError] If inline plugin compression is disabled
|
|
146
|
-
# @example Compress inline plugins
|
|
147
|
-
# try_compress_inline_plugins!
|
|
148
|
-
# @return [void]
|
|
149
|
-
# @note This method is called automatically when defining a preset
|
|
150
|
-
def try_compress_inline_plugins!
|
|
151
|
-
raise CompressionDisabledError if @disallow_inline_plugin_compression
|
|
152
|
-
|
|
153
|
-
config[:plugins].each do |plugin|
|
|
154
|
-
next unless plugin.is_a?(Editor::PropsInlinePlugin)
|
|
155
|
-
|
|
156
|
-
plugin.try_compress!
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
124
|
private
|
|
161
125
|
|
|
162
126
|
# Register a plugin in the editor configuration.
|