trix_embed 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 createURL(value, callback = url => {}) {
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 createURLHost(value, callback = host => {}) {
25
- let host = null
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 extractURLsFromTextNodes(element) {
32
- const urls = []
33
- const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, node => {
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 extractURLsFromElements(element) {
54
- const urls = []
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
- if (element.src) createURL(element.src, url => urls.push(url.href))
57
- if (element.href)
58
- createURL(element.href, url => {
59
- if (!urls.includes(url.href)) urls.push(url.href)
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
- let valid = false
74
- createURLHost(value, host => (valid = !!allowedHosts.find(allowedHost => host.includes(allowedHost))))
75
- return valid
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 values.reduce((hosts, value) => {
80
- createURLHost(value, host => {
81
- if (!hosts.includes(host)) hosts.push(host)
82
- })
83
- return hosts
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 extractURLsFromElement(element) {
93
- const elementURLs = extractURLsFromElements(element)
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 = ActionText::ContentHelper.allowed_attributes + %w[allow allowfullscreen allowpaymentrequest credentialless csp loading referrerpolicy sandbox srcdoc]
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 rewrite_action_text_content(content)
28
+ def rewrite_for_display(content)
15
29
  fragment = Nokogiri::HTML.fragment(content)
16
- matches = fragment.css("#{ActionText::Attachment.tag_name}[sgid][content-type='#{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 rewrite_trix_html(trix_html)
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='#{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.rewrite_action_text_content render_action_text_content(content) %>
1
+ <%= TrixEmbed::Attachment.rewrite_for_display render_action_text_content(content) %>
@@ -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 "application/vnd.trix-embed", :trix_embed
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TrixEmbed
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
  end
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.2
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-15 00:00:00.000000000 Z
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: A Stimulus controller to safely embed external media in the Trix editor
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: A Stimulus controller to safely embed external media in the Trix editor
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: []