actiontext 8.0.3 → 8.1.0.rc1
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/CHANGELOG.md +44 -71
- data/app/assets/javascripts/actiontext.esm.js +92 -14
- data/app/assets/javascripts/actiontext.js +98 -17
- data/app/helpers/action_text/tag_helper.rb +21 -8
- data/app/javascript/actiontext/attachment_upload.js +60 -11
- data/app/javascript/actiontext/index.js +10 -1
- data/app/models/action_text/rich_text.rb +5 -2
- data/lib/action_text/content.rb +2 -1
- data/lib/action_text/engine.rb +2 -1
- data/lib/action_text/fixture_set.rb +1 -1
- data/lib/action_text/gem_version.rb +3 -3
- data/lib/action_text/plain_text_conversion.rb +63 -28
- data/lib/action_text/rendering.rb +0 -1
- data/lib/action_text/system_test_helper.rb +17 -4
- data/lib/generators/action_text/install/install_generator.rb +0 -12
- data/package.json +2 -2
- metadata +26 -14
- data/app/assets/javascripts/trix.js +0 -13743
- data/app/assets/stylesheets/trix.css +0 -470
@@ -48,10 +48,13 @@ module ActionText
|
|
48
48
|
##
|
49
49
|
# :method: embeds
|
50
50
|
#
|
51
|
-
# Returns the ActiveStorage::
|
51
|
+
# Returns the ActiveStorage::Attachment records from the embedded files.
|
52
|
+
#
|
53
|
+
# Attached ActiveStorage::Blob records are extracted from the `body`
|
54
|
+
# in a {before_validation}[rdoc-ref:ActiveModel::Validations::Callbacks::ClassMethods#before_validation] callback.
|
52
55
|
has_many_attached :embeds
|
53
56
|
|
54
|
-
|
57
|
+
before_validation do
|
55
58
|
self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
|
56
59
|
end
|
57
60
|
|
data/lib/action_text/content.rb
CHANGED
@@ -123,10 +123,11 @@ module ActionText
|
|
123
123
|
# content.to_plain_text # => "safeunsafe"
|
124
124
|
#
|
125
125
|
# NOTE: that the returned string is not HTML safe and should not be rendered in
|
126
|
-
# browsers.
|
126
|
+
# browsers without additional sanitization.
|
127
127
|
#
|
128
128
|
# content = ActionText::Content.new("<script>alert()</script>")
|
129
129
|
# content.to_plain_text # => "<script>alert()</script>"
|
130
|
+
# ActionText::ContentHelper.sanitizer.sanitize(content.to_plain_text) # => ""
|
130
131
|
def to_plain_text
|
131
132
|
render_attachments(with_full_attributes: false, &:to_plain_text).fragment.to_plain_text
|
132
133
|
end
|
data/lib/action_text/engine.rb
CHANGED
@@ -8,6 +8,7 @@ require "active_record/railtie"
|
|
8
8
|
require "active_storage/engine"
|
9
9
|
|
10
10
|
require "action_text"
|
11
|
+
require "action_text/trix"
|
11
12
|
|
12
13
|
module ActionText
|
13
14
|
class Engine < Rails::Engine
|
@@ -34,7 +35,7 @@ module ActionText
|
|
34
35
|
|
35
36
|
initializer "action_text.asset" do
|
36
37
|
if Rails.application.config.respond_to?(:assets)
|
37
|
-
Rails.application.config.assets.precompile += %w( actiontext.js actiontext.esm.js
|
38
|
+
Rails.application.config.assets.precompile += %w( actiontext.js actiontext.esm.js )
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -62,7 +62,7 @@ module ActionText
|
|
62
62
|
signed_global_id = ActiveRecord::FixtureSet.signed_global_id fixture_set_name, label,
|
63
63
|
column_type: column_type, for: ActionText::Attachable::LOCATOR_NAME
|
64
64
|
|
65
|
-
%(
|
65
|
+
%(<#{Attachment.tag_name} sgid="#{signed_global_id}"></#{Attachment.tag_name}>)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -7,64 +7,70 @@ module ActionText
|
|
7
7
|
extend self
|
8
8
|
|
9
9
|
def node_to_plain_text(node)
|
10
|
-
|
10
|
+
BottomUpReducer.new(node).reduce do |n, child_values|
|
11
|
+
plain_text_for_node(n, child_values)
|
12
|
+
end.then(&method(:remove_trailing_newlines))
|
11
13
|
end
|
12
14
|
|
13
15
|
private
|
14
|
-
def plain_text_for_node(node,
|
16
|
+
def plain_text_for_node(node, child_values)
|
15
17
|
if respond_to?(plain_text_method_for_node(node), true)
|
16
|
-
send(plain_text_method_for_node(node), node,
|
18
|
+
send(plain_text_method_for_node(node), node, child_values)
|
17
19
|
else
|
18
|
-
|
20
|
+
plain_text_for_child_values(child_values)
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
def plain_text_for_node_children(node)
|
23
|
-
texts = []
|
24
|
-
node.children.each_with_index do |child, index|
|
25
|
-
texts << plain_text_for_node(child, index)
|
26
|
-
end
|
27
|
-
texts.join
|
28
|
-
end
|
29
|
-
|
30
24
|
def plain_text_method_for_node(node)
|
31
25
|
:"plain_text_for_#{node.name}_node"
|
32
26
|
end
|
33
27
|
|
34
|
-
def
|
35
|
-
|
28
|
+
def plain_text_for_child_values(child_values)
|
29
|
+
child_values.join
|
30
|
+
end
|
31
|
+
|
32
|
+
def plain_text_for_unsupported_node(node, _child_values)
|
33
|
+
""
|
34
|
+
end
|
35
|
+
|
36
|
+
%i[ script style].each do |element|
|
37
|
+
alias_method :"plain_text_for_#{element}_node", :plain_text_for_unsupported_node
|
38
|
+
end
|
39
|
+
|
40
|
+
def plain_text_for_block(node, child_values)
|
41
|
+
"#{remove_trailing_newlines(plain_text_for_child_values(child_values))}\n\n"
|
36
42
|
end
|
37
43
|
|
38
44
|
%i[ h1 p ].each do |element|
|
39
45
|
alias_method :"plain_text_for_#{element}_node", :plain_text_for_block
|
40
46
|
end
|
41
47
|
|
42
|
-
def plain_text_for_list(node,
|
43
|
-
"#{break_if_nested_list(node, plain_text_for_block(node))}"
|
48
|
+
def plain_text_for_list(node, child_values)
|
49
|
+
"#{break_if_nested_list(node, plain_text_for_block(node, child_values))}"
|
44
50
|
end
|
45
51
|
|
46
52
|
%i[ ul ol ].each do |element|
|
47
53
|
alias_method :"plain_text_for_#{element}_node", :plain_text_for_list
|
48
54
|
end
|
49
55
|
|
50
|
-
def plain_text_for_br_node(node,
|
56
|
+
def plain_text_for_br_node(node, _child_values)
|
51
57
|
"\n"
|
52
58
|
end
|
53
59
|
|
54
|
-
def plain_text_for_text_node(node,
|
60
|
+
def plain_text_for_text_node(node, _child_values)
|
55
61
|
remove_trailing_newlines(node.text)
|
56
62
|
end
|
57
63
|
|
58
|
-
def plain_text_for_div_node(node,
|
59
|
-
"#{remove_trailing_newlines(
|
64
|
+
def plain_text_for_div_node(node, child_values)
|
65
|
+
"#{remove_trailing_newlines(plain_text_for_child_values(child_values))}\n"
|
60
66
|
end
|
61
67
|
|
62
|
-
def plain_text_for_figcaption_node(node,
|
63
|
-
"[#{remove_trailing_newlines(
|
68
|
+
def plain_text_for_figcaption_node(node, child_values)
|
69
|
+
"[#{remove_trailing_newlines(plain_text_for_child_values(child_values))}]"
|
64
70
|
end
|
65
71
|
|
66
|
-
def plain_text_for_blockquote_node(node,
|
67
|
-
text = plain_text_for_block(node)
|
72
|
+
def plain_text_for_blockquote_node(node, child_values)
|
73
|
+
text = plain_text_for_block(node, child_values)
|
68
74
|
return "“”" if text.blank?
|
69
75
|
|
70
76
|
text = text.dup
|
@@ -73,9 +79,9 @@ module ActionText
|
|
73
79
|
text
|
74
80
|
end
|
75
81
|
|
76
|
-
def plain_text_for_li_node(node,
|
77
|
-
bullet = bullet_for_li_node(node
|
78
|
-
text = remove_trailing_newlines(
|
82
|
+
def plain_text_for_li_node(node, child_values)
|
83
|
+
bullet = bullet_for_li_node(node)
|
84
|
+
text = remove_trailing_newlines(plain_text_for_child_values(child_values))
|
79
85
|
indentation = indentation_for_li_node(node)
|
80
86
|
|
81
87
|
"#{indentation}#{bullet} #{text}\n"
|
@@ -85,8 +91,9 @@ module ActionText
|
|
85
91
|
text.chomp("")
|
86
92
|
end
|
87
93
|
|
88
|
-
def bullet_for_li_node(node
|
94
|
+
def bullet_for_li_node(node)
|
89
95
|
if list_node_name_for_li_node(node) == "ol"
|
96
|
+
index = node.parent.elements.index(node)
|
90
97
|
"#{index + 1}."
|
91
98
|
else
|
92
99
|
"•"
|
@@ -115,5 +122,33 @@ module ActionText
|
|
115
122
|
text
|
116
123
|
end
|
117
124
|
end
|
125
|
+
|
126
|
+
class BottomUpReducer # :nodoc:
|
127
|
+
def initialize(node)
|
128
|
+
@node = node
|
129
|
+
@values = {}
|
130
|
+
end
|
131
|
+
|
132
|
+
def reduce(&block)
|
133
|
+
traverse_bottom_up(@node) do |n|
|
134
|
+
child_values = @values.values_at(*n.children)
|
135
|
+
@values[n] = block.call(n, child_values)
|
136
|
+
end
|
137
|
+
@values[@node]
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def traverse_bottom_up(node, &block)
|
142
|
+
call_stack, processing_stack = [ node ], []
|
143
|
+
|
144
|
+
until call_stack.empty?
|
145
|
+
node = call_stack.pop
|
146
|
+
processing_stack.push(node)
|
147
|
+
call_stack.concat node.children
|
148
|
+
end
|
149
|
+
|
150
|
+
processing_stack.reverse_each(&block)
|
151
|
+
end
|
152
|
+
end
|
118
153
|
end
|
119
154
|
end
|
@@ -7,12 +7,14 @@ module ActionText
|
|
7
7
|
# Locates a Trix editor and fills it in with the given HTML.
|
8
8
|
#
|
9
9
|
# The editor can be found by:
|
10
|
+
#
|
10
11
|
# * its `id`
|
11
12
|
# * its `placeholder`
|
12
13
|
# * the text from its `label` element
|
13
14
|
# * its `aria-label`
|
14
15
|
# * the `name` of its input
|
15
16
|
#
|
17
|
+
# Additional options are forwarded to Capybara as filters
|
16
18
|
#
|
17
19
|
# Examples:
|
18
20
|
#
|
@@ -32,8 +34,14 @@ module ActionText
|
|
32
34
|
# # <input id="trix_input_1" name="message[content]" type="hidden">
|
33
35
|
# # <trix-editor input="trix_input_1"></trix-editor>
|
34
36
|
# fill_in_rich_textarea "message[content]", with: "Hello <em>world!</em>"
|
35
|
-
def fill_in_rich_textarea(locator = nil, with
|
36
|
-
find(:rich_textarea, locator).execute_script(
|
37
|
+
def fill_in_rich_textarea(locator = nil, with:, **)
|
38
|
+
find(:rich_textarea, locator, **).execute_script(<<~JS, with.to_s)
|
39
|
+
if ("value" in this) {
|
40
|
+
this.value = arguments[0]
|
41
|
+
} else {
|
42
|
+
this.editor.loadHTML(arguments[0])
|
43
|
+
}
|
44
|
+
JS
|
37
45
|
end
|
38
46
|
alias_method :fill_in_rich_text_area, :fill_in_rich_textarea
|
39
47
|
end
|
@@ -43,13 +51,18 @@ end
|
|
43
51
|
Capybara.add_selector rich_textarea do
|
44
52
|
label "rich-text area"
|
45
53
|
xpath do |locator|
|
54
|
+
xpath = XPath.descendant[[
|
55
|
+
XPath.attribute(:role) == "textbox",
|
56
|
+
(XPath.attribute(:contenteditable) == "") | (XPath.attribute(:contenteditable) == "true")
|
57
|
+
].reduce(:&)]
|
58
|
+
|
46
59
|
if locator.nil?
|
47
|
-
|
60
|
+
xpath
|
48
61
|
else
|
49
62
|
input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
|
50
63
|
input_located_by_label = XPath.anywhere(:label).where(XPath.string.n.is(locator)).attr(:for)
|
51
64
|
|
52
|
-
|
65
|
+
xpath.where \
|
53
66
|
XPath.attr(:id).equals(locator) |
|
54
67
|
XPath.attr(:placeholder).equals(locator) |
|
55
68
|
XPath.attr(:"aria-label").equals(locator) |
|
@@ -47,18 +47,6 @@ module ActionText
|
|
47
47
|
"app/views/layouts/action_text/contents/_content.html.erb"
|
48
48
|
end
|
49
49
|
|
50
|
-
def enable_image_processing_gem
|
51
|
-
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
|
52
|
-
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
|
53
|
-
image_processing_regex = /gem ["']image_processing["']/
|
54
|
-
if File.readlines(gemfile_path).grep(image_processing_regex).any?
|
55
|
-
uncomment_lines gemfile_path, image_processing_regex
|
56
|
-
else
|
57
|
-
run "bundle add --skip-install image_processing"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
50
|
def create_migrations
|
63
51
|
rails_command "railties:install:migrations FROM=active_storage,action_text", inline: true
|
64
52
|
end
|
data/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rails/actiontext",
|
3
|
-
"version": "8.0
|
3
|
+
"version": "8.1.0-rc1",
|
4
4
|
"description": "Edit and display rich text in Rails applications",
|
5
5
|
"module": "app/assets/javascripts/actiontext.esm.js",
|
6
6
|
"main": "app/assets/javascripts/actiontext.js",
|
@@ -22,7 +22,7 @@
|
|
22
22
|
],
|
23
23
|
"license": "MIT",
|
24
24
|
"dependencies": {
|
25
|
-
"@rails/activestorage": ">= 8.
|
25
|
+
"@rails/activestorage": ">= 8.1.0-alpha"
|
26
26
|
},
|
27
27
|
"peerDependencies": {
|
28
28
|
"trix": "^2.0.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actiontext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.
|
4
|
+
version: 8.1.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javan Makhmali
|
@@ -17,56 +17,56 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - '='
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 8.0.
|
20
|
+
version: 8.1.0.rc1
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - '='
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 8.0.
|
27
|
+
version: 8.1.0.rc1
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: activerecord
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - '='
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 8.0.
|
34
|
+
version: 8.1.0.rc1
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - '='
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 8.0.
|
41
|
+
version: 8.1.0.rc1
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: activestorage
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - '='
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 8.0.
|
48
|
+
version: 8.1.0.rc1
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - '='
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 8.0.
|
55
|
+
version: 8.1.0.rc1
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: actionpack
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
60
|
- - '='
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 8.0.
|
62
|
+
version: 8.1.0.rc1
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - '='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 8.0.
|
69
|
+
version: 8.1.0.rc1
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: nokogiri
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +95,20 @@ dependencies:
|
|
95
95
|
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: 0.6.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: action_text-trix
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 2.1.15
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.1.15
|
98
112
|
description: Edit and display rich text in Rails applications.
|
99
113
|
email:
|
100
114
|
- javan@javan.us
|
@@ -109,8 +123,6 @@ files:
|
|
109
123
|
- README.md
|
110
124
|
- app/assets/javascripts/actiontext.esm.js
|
111
125
|
- app/assets/javascripts/actiontext.js
|
112
|
-
- app/assets/javascripts/trix.js
|
113
|
-
- app/assets/stylesheets/trix.css
|
114
126
|
- app/helpers/action_text/content_helper.rb
|
115
127
|
- app/helpers/action_text/tag_helper.rb
|
116
128
|
- app/javascript/actiontext/attachment_upload.js
|
@@ -162,10 +174,10 @@ licenses:
|
|
162
174
|
- MIT
|
163
175
|
metadata:
|
164
176
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
165
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.0.
|
166
|
-
documentation_uri: https://api.rubyonrails.org/v8.0.
|
177
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.1.0.rc1/actiontext/CHANGELOG.md
|
178
|
+
documentation_uri: https://api.rubyonrails.org/v8.1.0.rc1/
|
167
179
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
168
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.0.
|
180
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.1.0.rc1/actiontext
|
169
181
|
rubygems_mfa_required: 'true'
|
170
182
|
rdoc_options: []
|
171
183
|
require_paths:
|