trix_embed 0.0.2 → 0.0.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.
- checksums.yaml +4 -4
- data/README.md +120 -47
- data/app/assets/builds/trix-embed.js +38 -19
- data/app/assets/builds/trix-embed.metafile.json +1 -1
- data/app/javascript/controller.js +342 -145
- data/app/javascript/enumerable.js +19 -0
- data/app/javascript/forms.js +134 -0
- data/app/javascript/guard.js +29 -47
- data/app/javascript/index.js +9 -3
- data/app/javascript/media.js +12 -2
- data/app/javascript/metadata.js +4 -0
- data/app/javascript/renderer.js +155 -75
- data/app/javascript/store.js +13 -1
- data/app/javascript/templates.js +45 -28
- data/app/javascript/urls.js +57 -42
- data/app/models/trix_embed/attachment.rb +20 -6
- data/app/views/action_text/contents/_content.html.erb +1 -1
- data/lib/trix_embed/engine.rb +1 -1
- data/lib/trix_embed/version.rb +1 -1
- metadata +37 -4
data/app/javascript/urls.js
CHANGED
@@ -4,10 +4,10 @@
|
|
4
4
|
// @param {Function} callback - Function to be called with the URL object
|
5
5
|
// @returns {URL, null} URL object
|
6
6
|
//
|
7
|
-
export function
|
7
|
+
export function createURLObject(value, callback = url => {}) {
|
8
8
|
try {
|
9
9
|
const url = new URL(String(value).trim())
|
10
|
-
if (callback) callback(url)
|
10
|
+
if (url && callback) callback(url)
|
11
11
|
return url
|
12
12
|
} catch (_error) {
|
13
13
|
console.info(`Failed to parse URL! value='${value}']`)
|
@@ -21,67 +21,82 @@ export function createURL(value, callback = url => {}) {
|
|
21
21
|
// @param {Function} callback - Function to be called with the URL host
|
22
22
|
// @returns {String, null} URL host
|
23
23
|
//
|
24
|
-
function
|
25
|
-
let host =
|
26
|
-
createURL(value, url => (host = url.host))
|
24
|
+
function extractURLHost(value, callback = host => {}) {
|
25
|
+
let host = createURLObject(value)?.host
|
27
26
|
if (host && callback) callback(host)
|
28
27
|
return host
|
29
28
|
}
|
30
29
|
|
31
|
-
function
|
32
|
-
|
33
|
-
|
34
|
-
const value = node.nodeValue
|
35
|
-
if (!value.includes('http')) return NodeFilter.FILTER_REJECT
|
36
|
-
return NodeFilter.FILTER_ACCEPT
|
30
|
+
export function createURLTextNodeTreeWalker(element) {
|
31
|
+
return document.createTreeWalker(element, NodeFilter.SHOW_TEXT, node => {
|
32
|
+
return node.nodeValue.match(/http/gi) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
37
33
|
})
|
34
|
+
}
|
35
|
+
|
36
|
+
function extractURLsFromTextNodes(element) {
|
37
|
+
const urls = new Set()
|
38
|
+
const walker = createURLTextNodeTreeWalker(element)
|
38
39
|
|
39
40
|
let node
|
40
41
|
while ((node = walker.nextNode()))
|
41
42
|
node.nodeValue
|
42
43
|
.split(/\s+/)
|
43
44
|
.filter(val => val.startsWith('http'))
|
44
|
-
.forEach(match =>
|
45
|
-
createURL(match, url => {
|
46
|
-
if (!urls.includes(url.href)) urls.push(url.href)
|
47
|
-
})
|
48
|
-
)
|
45
|
+
.forEach(match => createURLObject(match, url => urls.add(url.href)))
|
49
46
|
|
50
|
-
return urls
|
47
|
+
return [...urls]
|
51
48
|
}
|
52
49
|
|
53
|
-
function
|
54
|
-
|
50
|
+
export function extractURLFromElement(element) {
|
51
|
+
if (element.src) {
|
52
|
+
const url = element.src.trim()
|
53
|
+
if (url.length) return url
|
54
|
+
}
|
55
|
+
|
56
|
+
if (element.href) {
|
57
|
+
const url = element.href.trim()
|
58
|
+
if (url.length) return url
|
59
|
+
}
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
return ''
|
62
|
+
}
|
63
|
+
|
64
|
+
function extractURLsFromElementNodes(element) {
|
65
|
+
const urls = new Set()
|
66
|
+
|
67
|
+
if (element.src) createURLObject(element.src, url => urls.add(url.href))
|
68
|
+
if (element.href) createURLObject(element.href, url => urls.add(url.href))
|
61
69
|
|
62
70
|
const elements = element.querySelectorAll('[src], [href]')
|
63
|
-
elements.forEach(el =>
|
64
|
-
createURL(el.src || el.href, url => {
|
65
|
-
if (!urls.includes(url.href)) urls.push(url.href)
|
66
|
-
})
|
67
|
-
})
|
71
|
+
elements.forEach(el => createURLObject(extractURLFromElement(el), u => urls.add(u.href)))
|
68
72
|
|
69
|
-
return urls
|
73
|
+
return [...urls]
|
70
74
|
}
|
71
75
|
|
72
|
-
export function validateURL(value, allowedHosts = []) {
|
73
|
-
|
74
|
-
|
75
|
-
return
|
76
|
+
export function validateURL(value, allowedHosts = [], blockedHosts = []) {
|
77
|
+
const host = extractURLHost(value)
|
78
|
+
|
79
|
+
if (blockedHosts.includes('*')) return false
|
80
|
+
if (blockedHosts.find(blockedHosts => host.endsWith(blockedHosts))) return false
|
81
|
+
if (allowedHosts.find(allowedHosts => host.endsWith(allowedHosts))) return true
|
82
|
+
|
83
|
+
if (allowedHosts.includes('*')) {
|
84
|
+
if (host) return true
|
85
|
+
if (value.startsWith('data:')) return true
|
86
|
+
if (value.startsWith('news:')) return true
|
87
|
+
if (value.startsWith('tel:')) return true
|
88
|
+
}
|
89
|
+
|
90
|
+
return false
|
76
91
|
}
|
77
92
|
|
78
93
|
export function extractURLHosts(values) {
|
79
|
-
return
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
94
|
+
return [
|
95
|
+
...values.reduce((hosts, value) => {
|
96
|
+
extractURLHost(value, host => hosts.add(host))
|
97
|
+
return hosts
|
98
|
+
}, new Set())
|
99
|
+
]
|
85
100
|
}
|
86
101
|
|
87
102
|
// Extracts all URLs from an HTML element (all inclusive i.e. elements and text nodes)
|
@@ -89,8 +104,8 @@ export function extractURLHosts(values) {
|
|
89
104
|
// @param {HTMLElement} element - HTML element
|
90
105
|
// @returns {String[]} list of unique URLs
|
91
106
|
//
|
92
|
-
export function
|
93
|
-
const elementURLs =
|
107
|
+
export function extractURLs(element) {
|
108
|
+
const elementURLs = extractURLsFromElementNodes(element)
|
94
109
|
const textNodeURLs = extractURLsFromTextNodes(element)
|
95
110
|
const uniqueURLs = new Set([...elementURLs, ...textNodeURLs])
|
96
111
|
return [...uniqueURLs]
|
@@ -6,14 +6,28 @@ module TrixEmbed
|
|
6
6
|
include GlobalID::Identification
|
7
7
|
include ActionText::Attachable
|
8
8
|
|
9
|
-
CONTENT_TYPE = "application/vnd.trix-embed"
|
10
9
|
ALLOWED_TAGS = ActionText::ContentHelper.allowed_tags + %w[iframe]
|
11
|
-
ALLOWED_ATTRIBUTES =
|
10
|
+
ALLOWED_ATTRIBUTES = (
|
11
|
+
ActionText::ContentHelper.allowed_attributes + %w[
|
12
|
+
allow
|
13
|
+
allowfullscreen
|
14
|
+
allowpaymentrequest
|
15
|
+
credentialless
|
16
|
+
csp
|
17
|
+
data-trix-embed
|
18
|
+
data-trix-embed-error
|
19
|
+
data-trix-embed-prohibited
|
20
|
+
data-trix-embed-warning
|
21
|
+
loading
|
22
|
+
referrerpolicy
|
23
|
+
sandbox
|
24
|
+
srcdoc
|
25
|
+
]) - %w[class style]
|
12
26
|
|
13
27
|
class << self
|
14
|
-
def
|
28
|
+
def rewrite_for_display(content)
|
15
29
|
fragment = Nokogiri::HTML.fragment(content)
|
16
|
-
matches = fragment.css("#{ActionText::Attachment.tag_name}[sgid][content-type='#{
|
30
|
+
matches = fragment.css("#{ActionText::Attachment.tag_name}[sgid][content-type='#{Mime::Type.lookup_by_extension(:trix_embed_attachment)}']")
|
17
31
|
|
18
32
|
matches.each do |match|
|
19
33
|
attachment = ActionText::Attachment.from_node(match)
|
@@ -37,9 +51,9 @@ module TrixEmbed
|
|
37
51
|
fragment.to_html.html_safe
|
38
52
|
end
|
39
53
|
|
40
|
-
def
|
54
|
+
def rewrite_for_storage(trix_html)
|
41
55
|
fragment = Nokogiri::HTML.fragment(trix_html)
|
42
|
-
matches = fragment.css("[data-trix-attachment][data-trix-content-type='#{
|
56
|
+
matches = fragment.css("[data-trix-attachment][data-trix-content-type='#{Mime::Type.lookup_by_extension(:trix_embed_attachment)}']")
|
43
57
|
|
44
58
|
matches.each do |match|
|
45
59
|
data = JSON.parse(match["data-trix-attachment"]).deep_transform_keys(&:underscore)
|
@@ -1 +1 @@
|
|
1
|
-
<%= TrixEmbed::Attachment.
|
1
|
+
<%= TrixEmbed::Attachment.rewrite_for_display render_action_text_content(content) %>
|
data/lib/trix_embed/engine.rb
CHANGED
@@ -16,7 +16,7 @@ module TrixEmbed
|
|
16
16
|
config.trix_embed = ActiveSupport::OrderedOptions.new
|
17
17
|
|
18
18
|
initializer "trix_embed.configuration" do
|
19
|
-
Mime::Type.register "
|
19
|
+
Mime::Type.register "trix-embed/attachment", :trix_embed_attachment
|
20
20
|
|
21
21
|
ActiveSupport.on_load :action_controller do
|
22
22
|
helper TrixEmbed::ApplicationHelper
|
data/lib/trix_embed/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trix_embed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Hopkins (hopsoft)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.15'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: amazing_print
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: capybara
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,6 +234,20 @@ dependencies:
|
|
220
234
|
- - ">="
|
221
235
|
- !ruby/object:Gem::Version
|
222
236
|
version: '0'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: rerun
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
244
|
+
type: :development
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
223
251
|
- !ruby/object:Gem::Dependency
|
224
252
|
name: rexml
|
225
253
|
requirement: !ruby/object:Gem::Requirement
|
@@ -304,7 +332,8 @@ dependencies:
|
|
304
332
|
- - ">="
|
305
333
|
- !ruby/object:Gem::Version
|
306
334
|
version: '0'
|
307
|
-
description:
|
335
|
+
description: Take control over what external links and embedded media is permitted
|
336
|
+
in the Trix editor via copy/paste
|
308
337
|
email:
|
309
338
|
- natehop@gmail.com
|
310
339
|
executables: []
|
@@ -318,9 +347,12 @@ files:
|
|
318
347
|
- app/helpers/trix_embed/application_helper.rb
|
319
348
|
- app/javascript/controller.js
|
320
349
|
- app/javascript/encryption.js
|
350
|
+
- app/javascript/enumerable.js
|
351
|
+
- app/javascript/forms.js
|
321
352
|
- app/javascript/guard.js
|
322
353
|
- app/javascript/index.js
|
323
354
|
- app/javascript/media.js
|
355
|
+
- app/javascript/metadata.js
|
324
356
|
- app/javascript/renderer.js
|
325
357
|
- app/javascript/store.js
|
326
358
|
- app/javascript/templates.js
|
@@ -358,5 +390,6 @@ requirements: []
|
|
358
390
|
rubygems_version: 3.1.6
|
359
391
|
signing_key:
|
360
392
|
specification_version: 4
|
361
|
-
summary:
|
393
|
+
summary: Take control over what external links and embedded media is permitted in
|
394
|
+
the Trix editor via copy/paste
|
362
395
|
test_files: []
|