draftjs_html 0.21.0 → 0.22.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/lib/draftjs_html/from_html/char_list.rb +152 -0
- data/lib/draftjs_html/from_html/depth_stack.rb +12 -12
- data/lib/draftjs_html/from_html/pending_block.rb +13 -27
- data/lib/draftjs_html/from_html.rb +1 -1
- data/lib/draftjs_html/version.rb +1 -1
- metadata +4 -4
- data/lib/draftjs_html/from_html/style_stack.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84ac3de7f21860685dbf39a24fb921227a1ccce6d97940acd279976daab15970
|
4
|
+
data.tar.gz: 2d3511eef3134a6cc01cd3da0147b3c347209c6c90ef4f5aaea90c681af979b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2775eac524063149973d4908f9f30f5e9a4d6b734f35e44b459454a95cc6a415a25a1b36daf07af3b34b1bf08b2b96c2980e6be12a9b21ed23c162100688ad03
|
7
|
+
data.tar.gz: dd0c42f5271934a1a17452159e7dbad8b51bd4ad0637a9b87cf4c77b43c6ea690bdda838830d92b1fcd929e985d88958fa5bd5326af89dc3e9db76e7e84a5fe2
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module DraftjsHtml
|
5
|
+
class FromHtml < Nokogiri::XML::SAX::Document
|
6
|
+
class CharList
|
7
|
+
module FinishableRange
|
8
|
+
def range
|
9
|
+
start..finish
|
10
|
+
end
|
11
|
+
|
12
|
+
def try_finish(index)
|
13
|
+
self[:finish] ||= index
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Char = Struct.new(:it, :styles, :entity, :atomic, keyword_init: true) do
|
18
|
+
def atomic?
|
19
|
+
self.atomic
|
20
|
+
end
|
21
|
+
|
22
|
+
def styles
|
23
|
+
self[:styles] ||= Set.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
EntityRange = Struct.new(:entity, :start, :finish, keyword_init: true) do
|
28
|
+
include FinishableRange
|
29
|
+
end
|
30
|
+
|
31
|
+
StyleRange = Struct.new(:style, :start, :finish, keyword_init: true) do
|
32
|
+
include FinishableRange
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :chars
|
36
|
+
|
37
|
+
def initialize(initial = [])
|
38
|
+
@chars = initial.dup
|
39
|
+
end
|
40
|
+
|
41
|
+
def append(str, styles: Set.new, entity: nil)
|
42
|
+
@chars << Char.new(it: "\n") if @chars.last&.atomic?
|
43
|
+
|
44
|
+
@chars += str.chars.map { Char.new(it: _1, styles: Set.new(styles), entity: entity) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def append_char(char)
|
48
|
+
@chars << char
|
49
|
+
end
|
50
|
+
|
51
|
+
def text
|
52
|
+
@chars.map(&:it).join
|
53
|
+
end
|
54
|
+
|
55
|
+
def size
|
56
|
+
@chars.size
|
57
|
+
end
|
58
|
+
|
59
|
+
def any?
|
60
|
+
size > 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def atomic?
|
64
|
+
@chars.any? && @chars.all?(&:atomic?)
|
65
|
+
end
|
66
|
+
|
67
|
+
def each_line
|
68
|
+
return to_enum(:each_line) unless block_given?
|
69
|
+
|
70
|
+
line = self.class.new
|
71
|
+
chars.each do |c|
|
72
|
+
if c.it == "\n"
|
73
|
+
yield line
|
74
|
+
line = self.class.new
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
line.append_char(c)
|
79
|
+
end
|
80
|
+
|
81
|
+
yield line if line.any?
|
82
|
+
end
|
83
|
+
|
84
|
+
def apply_entity(range, entity)
|
85
|
+
@chars[range].each { _1.entity = entity }
|
86
|
+
end
|
87
|
+
|
88
|
+
def append_atomic_entity(entity)
|
89
|
+
append("\n") if @chars.any?
|
90
|
+
append_char(Char.new(it: ' ', atomic: true, entity: entity))
|
91
|
+
end
|
92
|
+
|
93
|
+
def append_styles(range, styles)
|
94
|
+
@chars[range].each { _1.styles += Array(styles) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def +(other)
|
98
|
+
self.class.new(@chars + other.chars)
|
99
|
+
end
|
100
|
+
|
101
|
+
def entity_ranges
|
102
|
+
current_entity = nil
|
103
|
+
|
104
|
+
entity_ranges = @chars.each_with_object(Array.new).with_index do |(char, ranges), i|
|
105
|
+
next if char.entity == current_entity
|
106
|
+
|
107
|
+
current_entity = char.entity
|
108
|
+
ranges.last&.try_finish(i - 1)
|
109
|
+
ranges << EntityRange.new(entity: char.entity, start: i) if char.entity
|
110
|
+
end
|
111
|
+
|
112
|
+
entity_ranges.last&.try_finish(@chars.size - 1)
|
113
|
+
entity_ranges
|
114
|
+
end
|
115
|
+
|
116
|
+
def style_ranges
|
117
|
+
style_ranges = @chars.each_with_object({}).with_index do |(char, ranges_by_style), i|
|
118
|
+
char.styles.each do |style|
|
119
|
+
ranges_by_style[style] ||= []
|
120
|
+
if i > 0 && @chars[i-1].styles.include?(style)
|
121
|
+
ranges_by_style[style].last << i
|
122
|
+
else
|
123
|
+
ranges_by_style[style] << [i]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
style_ranges.flat_map do |style, ranges|
|
129
|
+
ranges.map do |range|
|
130
|
+
StyleRange.new(style: style, start: range.first, finish: range.last)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def find_overlapping_styles(descriptors)
|
138
|
+
descriptors.select do |candidate_a|
|
139
|
+
candidate_range = candidate_a[:start]..candidate_a[:finish]
|
140
|
+
(descriptors - [candidate_a]).any? do |other|
|
141
|
+
other_range = other[:start]..other[:finish]
|
142
|
+
range_overlaps?(candidate_range, other_range)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def range_overlaps?(candidate_range, other_range)
|
148
|
+
other_range.begin == candidate_range.begin || candidate_range.cover?(other_range.begin) || other_range.cover?(candidate_range.begin)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -5,7 +5,7 @@ module DraftjsHtml
|
|
5
5
|
@stack = []
|
6
6
|
@nodes = []
|
7
7
|
@list_depth = -1
|
8
|
-
@
|
8
|
+
@active_styles = []
|
9
9
|
end
|
10
10
|
|
11
11
|
def push(tagname, attrs)
|
@@ -26,8 +26,7 @@ module DraftjsHtml
|
|
26
26
|
@nodes.pop
|
27
27
|
end
|
28
28
|
blocks.reverse_each do |pending_block|
|
29
|
-
pending_block.flush_to(draftjs
|
30
|
-
pending_block.apply_entities_to(draftjs)
|
29
|
+
pending_block.flush_to(draftjs)
|
31
30
|
end
|
32
31
|
@list_depth -= 1
|
33
32
|
end
|
@@ -59,34 +58,35 @@ module DraftjsHtml
|
|
59
58
|
next unless user_created_entity
|
60
59
|
|
61
60
|
if content == '' && !user_created_entity[:atomic]
|
62
|
-
current.text_buffer
|
63
|
-
|
61
|
+
current.text_buffer.append(' ', entity: user_created_entity)
|
62
|
+
elsif content == '' && user_created_entity[:atomic]
|
63
|
+
current.text_buffer.append_atomic_entity(user_created_entity)
|
64
|
+
else
|
65
|
+
current.text_buffer.apply_entity(range, user_created_entity)
|
64
66
|
end
|
65
|
-
current.entities << user_created_entity.merge(start: range.begin, finish: range.end)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
69
70
|
def style_start(tagname)
|
70
|
-
@
|
71
|
+
@active_styles += [DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]]
|
71
72
|
end
|
72
73
|
|
73
74
|
def style_end(tagname)
|
74
|
-
@
|
75
|
+
@active_styles.delete_at(@active_styles.index(DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]))
|
75
76
|
end
|
76
77
|
|
77
78
|
def flush_to(draftjs)
|
78
|
-
current.flush_to(draftjs
|
79
|
-
current.apply_entities_to(draftjs)
|
79
|
+
current.flush_to(draftjs)
|
80
80
|
end
|
81
81
|
|
82
82
|
def append_text(chars)
|
83
|
-
current.text_buffer
|
83
|
+
current.text_buffer.append(chars, styles: @active_styles) unless chars.empty?
|
84
84
|
end
|
85
85
|
|
86
86
|
private
|
87
87
|
|
88
88
|
def current_text_buffer
|
89
|
-
current.text_buffer.
|
89
|
+
current.text_buffer.text
|
90
90
|
end
|
91
91
|
|
92
92
|
def current_character_offset
|
@@ -6,7 +6,7 @@ module DraftjsHtml
|
|
6
6
|
tagname: name,
|
7
7
|
attrs: attrs,
|
8
8
|
entities: [],
|
9
|
-
chars:
|
9
|
+
chars: CharList.new,
|
10
10
|
pending_entities: [],
|
11
11
|
depth: depth,
|
12
12
|
parent_tagnames: parent_tagnames,
|
@@ -17,12 +17,8 @@ module DraftjsHtml
|
|
17
17
|
self[:chars]
|
18
18
|
end
|
19
19
|
|
20
|
-
def clear_text_buffer
|
21
|
-
self[:chars] = []
|
22
|
-
end
|
23
|
-
|
24
20
|
def character_offset
|
25
|
-
text_buffer.
|
21
|
+
text_buffer.size - 1
|
26
22
|
end
|
27
23
|
|
28
24
|
def flushable?
|
@@ -36,31 +32,21 @@ module DraftjsHtml
|
|
36
32
|
self.entities += other_pending_block.entities
|
37
33
|
end
|
38
34
|
|
39
|
-
def flush_to(draftjs
|
35
|
+
def flush_to(draftjs)
|
40
36
|
if text_buffer.any?
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
styles.each do |descriptor|
|
46
|
-
finish = descriptor[:finish] || character_offset
|
47
|
-
draftjs.inline_style(descriptor[:style], descriptor[:start]..finish)
|
48
|
-
end
|
49
|
-
end
|
37
|
+
text_buffer.each_line do |line|
|
38
|
+
block_type = line.atomic? ? 'atomic' : block_name
|
39
|
+
draftjs.typed_block(block_type, line.text, depth: [depth, 0].max)
|
50
40
|
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
line.entity_ranges.each do |entity_range|
|
42
|
+
entity = entity_range.entity
|
43
|
+
draftjs.apply_entity entity[:type], entity_range.range, data: entity[:data], mutability: entity.fetch(:mutability, 'IMMUTABLE')
|
44
|
+
end
|
54
45
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
if entity[:atomic]
|
59
|
-
draftjs.typed_block('atomic', ' ', depth: [depth, 0].max)
|
60
|
-
range = 0..0
|
46
|
+
line.style_ranges.each do |style_range|
|
47
|
+
draftjs.inline_style(style_range.style, style_range.range)
|
48
|
+
end
|
61
49
|
end
|
62
|
-
|
63
|
-
draftjs.apply_entity entity[:type], range, data: entity[:data], mutability: entity.fetch(:mutability, 'IMMUTABLE')
|
64
50
|
end
|
65
51
|
end
|
66
52
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require_relative 'html_defaults'
|
3
3
|
require_relative 'from_html/elements'
|
4
|
-
require_relative 'from_html/style_stack'
|
5
4
|
require_relative 'from_html/pending_block'
|
6
5
|
require_relative 'from_html/depth_stack'
|
6
|
+
require_relative 'from_html/char_list'
|
7
7
|
|
8
8
|
module DraftjsHtml
|
9
9
|
class FromHtml < Nokogiri::XML::SAX::Document
|
data/lib/draftjs_html/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: draftjs_html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TJ Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -68,10 +68,10 @@ files:
|
|
68
68
|
- lib/draftjs_html/draftjs/raw_builder.rb
|
69
69
|
- lib/draftjs_html/draftjs/to_raw.rb
|
70
70
|
- lib/draftjs_html/from_html.rb
|
71
|
+
- lib/draftjs_html/from_html/char_list.rb
|
71
72
|
- lib/draftjs_html/from_html/depth_stack.rb
|
72
73
|
- lib/draftjs_html/from_html/elements.rb
|
73
74
|
- lib/draftjs_html/from_html/pending_block.rb
|
74
|
-
- lib/draftjs_html/from_html/style_stack.rb
|
75
75
|
- lib/draftjs_html/html_defaults.rb
|
76
76
|
- lib/draftjs_html/html_depth.rb
|
77
77
|
- lib/draftjs_html/node.rb
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
103
|
+
rubygems_version: 3.3.26
|
104
104
|
signing_key:
|
105
105
|
specification_version: 4
|
106
106
|
summary: A tool for converting DraftJS JSON to HTML (and back again)
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module DraftjsHtml
|
2
|
-
class FromHtml < Nokogiri::XML::SAX::Document
|
3
|
-
class StyleStack
|
4
|
-
def initialize
|
5
|
-
@stack = []
|
6
|
-
end
|
7
|
-
|
8
|
-
def clear_finished
|
9
|
-
@stack.delete_if { !!_1[:finish] }
|
10
|
-
end
|
11
|
-
|
12
|
-
def each(&block)
|
13
|
-
@stack.reverse_each.group_by { _1[:tagname] }.each do |_, descriptors|
|
14
|
-
overlapping_ranges = find_overlapping_styles(descriptors)
|
15
|
-
widest_descriptor = overlapping_ranges.max_by { (_1[:start].._1[:finish]).size }
|
16
|
-
|
17
|
-
applicable_styles = descriptors - overlapping_ranges + [widest_descriptor].compact
|
18
|
-
applicable_styles.each(&block)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def track_start(tagname, current_character_offset)
|
23
|
-
style = DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]
|
24
|
-
@stack.unshift({ tagname: tagname, style: style, start: current_character_offset })
|
25
|
-
end
|
26
|
-
|
27
|
-
def track_end(tagname, current_character_offset)
|
28
|
-
descriptor_index = @stack.find_index { _1[:tagname] == tagname && !_1[:finish] }
|
29
|
-
descriptor = @stack[descriptor_index]
|
30
|
-
descriptor[:finish] = current_character_offset
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def find_overlapping_styles(descriptors)
|
36
|
-
descriptors.select do |candidate_a|
|
37
|
-
candidate_range = candidate_a[:start]..candidate_a[:finish]
|
38
|
-
(descriptors - [candidate_a]).any? do |other|
|
39
|
-
other_range = other[:start]..other[:finish]
|
40
|
-
range_overlaps?(candidate_range, other_range)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def range_overlaps?(candidate_range, other_range)
|
46
|
-
other_range.begin == candidate_range.begin || candidate_range.cover?(other_range.begin) || other_range.cover?(candidate_range.begin)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|