ckeditor5 1.9.0 → 1.11.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 +4 -4
- data/Gemfile +10 -0
- data/README.md +63 -11
- data/lib/ckeditor5/rails/assets/assets_bundle_html_serializer.rb +4 -4
- data/lib/ckeditor5/rails/assets/webcomponent_bundle.rb +22 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/context.mjs +113 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/editable.mjs +113 -0
- data/lib/ckeditor5/rails/assets/{webcomponent.mjs → webcomponents/components/editor.mjs} +104 -479
- data/lib/ckeditor5/rails/assets/webcomponents/components/ui-part.mjs +26 -0
- data/lib/ckeditor5/rails/assets/webcomponents/utils.mjs +155 -0
- data/lib/ckeditor5/rails/cdn/ckbox_bundle.rb +17 -8
- data/lib/ckeditor5/rails/cdn/helpers.rb +3 -11
- data/lib/ckeditor5/rails/cdn/url_generator.rb +7 -5
- data/lib/ckeditor5/rails/helpers.rb +0 -2
- data/lib/ckeditor5/rails/presets/preset_builder.rb +21 -2
- data/lib/ckeditor5/rails/version.rb +1 -1
- metadata +8 -4
- data/lib/ckeditor5/rails/cloud/helpers.rb +0 -13
@@ -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
|
+
}
|
@@ -8,38 +8,47 @@ module CKEditor5::Rails
|
|
8
8
|
attr_reader :cdn, :version, :theme, :translations
|
9
9
|
|
10
10
|
def initialize(version, theme: :lark, cdn: Engine.default_preset.cdn, translations: [])
|
11
|
-
raise ArgumentError, 'version must be semver' unless version.is_a?(Semver)
|
12
|
-
raise ArgumentError, 'theme must be a string' unless theme.is_a?(String)
|
13
|
-
raise ArgumentError, 'translations must be an array' unless translations.is_a?(Array)
|
14
|
-
|
15
11
|
super()
|
16
12
|
|
17
13
|
@cdn = cdn
|
18
14
|
@version = version
|
19
15
|
@theme = theme
|
20
16
|
@translations = translations
|
17
|
+
|
18
|
+
validate!
|
21
19
|
end
|
22
20
|
|
23
21
|
def scripts
|
24
22
|
@scripts ||= [
|
25
23
|
Assets::JSExportsMeta.new(
|
26
|
-
create_cdn_url('ckbox', 'ckbox.js'
|
27
|
-
*translations_js_exports_meta
|
24
|
+
create_cdn_url('ckbox', version, 'ckbox.js'),
|
25
|
+
*translations_js_exports_meta,
|
26
|
+
window_name: 'CKBox'
|
28
27
|
)
|
29
28
|
]
|
30
29
|
end
|
31
30
|
|
32
31
|
def stylesheets
|
33
32
|
@stylesheets ||= [
|
34
|
-
create_cdn_url('ckbox', "styles/themes/#{theme}.css"
|
33
|
+
create_cdn_url('ckbox', version, "styles/themes/#{theme}.css")
|
35
34
|
]
|
36
35
|
end
|
37
36
|
|
38
37
|
private
|
39
38
|
|
39
|
+
def validate!
|
40
|
+
raise ArgumentError, 'version must be semver' unless version.is_a?(Semver)
|
41
|
+
raise ArgumentError, 'translations must be an array' unless translations.is_a?(Array)
|
42
|
+
|
43
|
+
return if theme.is_a?(String) || theme.is_a?(Symbol)
|
44
|
+
|
45
|
+
raise ArgumentError,
|
46
|
+
'theme must be a string or symbol'
|
47
|
+
end
|
48
|
+
|
40
49
|
def translations_js_exports_meta
|
41
50
|
translations.map do |lang|
|
42
|
-
url = create_cdn_url('ckbox', "translations/#{lang}.js"
|
51
|
+
url = create_cdn_url('ckbox', version, "translations/#{lang}.js")
|
43
52
|
|
44
53
|
Assets::JSExportsMeta.new(url, window_name: 'CKBOX_TRANSLATIONS', translation: true)
|
45
54
|
end
|
@@ -6,7 +6,7 @@ require_relative 'ckbox_bundle'
|
|
6
6
|
|
7
7
|
module CKEditor5::Rails
|
8
8
|
module Cdn::Helpers
|
9
|
-
def
|
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
|
-
|
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
|
-
"`
|
57
|
+
"`ckeditor5_assets` or defined default one in your `#{preset}` preset!"
|
66
58
|
end
|
67
59
|
|
68
60
|
hash
|
@@ -20,9 +20,11 @@ module CKEditor5::Rails::Cdn
|
|
20
20
|
|
21
21
|
CDN_COMMERCIAL_GENERATORS = {
|
22
22
|
cloud: lambda { |bundle, version, path|
|
23
|
-
|
23
|
+
"https://cdn.ckeditor.com/#{bundle}/#{version}/#{path}"
|
24
|
+
},
|
24
25
|
|
25
|
-
|
26
|
+
ckbox: lambda { |bundle, version, path|
|
27
|
+
"https://cdn.ckbox.io/#{bundle}/#{version}/#{path}"
|
26
28
|
}
|
27
29
|
}.freeze
|
28
30
|
|
@@ -31,11 +33,11 @@ module CKEditor5::Rails::Cdn
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def create_cdn_url(bundle, version, path)
|
34
|
-
|
36
|
+
executor = CDN_THIRD_PARTY_GENERATORS[cdn] || CDN_COMMERCIAL_GENERATORS[cdn] || cdn
|
35
37
|
|
36
|
-
raise ArgumentError, "Unknown provider: #{cdn}"
|
38
|
+
raise ArgumentError, "Unknown provider: #{cdn}" if executor.blank? || !executor.respond_to?(:call)
|
37
39
|
|
38
|
-
|
40
|
+
executor.call(bundle, version, path)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -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),
|
@@ -44,13 +56,18 @@ module CKEditor5::Rails
|
|
44
56
|
def ckbox(version = nil, theme: :lark)
|
45
57
|
return @ckbox if version.nil?
|
46
58
|
|
47
|
-
@ckbox = {
|
59
|
+
@ckbox = {
|
60
|
+
version: version,
|
61
|
+
theme: theme
|
62
|
+
}
|
48
63
|
end
|
49
64
|
|
50
65
|
def license_key(license_key = nil)
|
51
66
|
return @license_key if license_key.nil?
|
52
67
|
|
53
68
|
@license_key = license_key
|
69
|
+
|
70
|
+
cdn(:cloud) unless gpl?
|
54
71
|
end
|
55
72
|
|
56
73
|
def gpl
|
@@ -134,7 +151,9 @@ module CKEditor5::Rails
|
|
134
151
|
names.each { |name| plugin(name, **kwargs) }
|
135
152
|
end
|
136
153
|
|
137
|
-
def language(ui, content: ui) # rubocop:disable Naming/MethodParameterName
|
154
|
+
def language(ui = nil, content: ui) # rubocop:disable Naming/MethodParameterName
|
155
|
+
return @config[:language] if ui.nil?
|
156
|
+
|
138
157
|
@config[:language] = {
|
139
158
|
ui: ui,
|
140
159
|
content: content
|
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.
|
4
|
+
version: 1.11.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-
|
12
|
+
date: 2024-11-20 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/
|
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
|