module DraftjsHtml
class FromHtml < Nokogiri::XML::SAX::Document
class DepthStack
def initialize
@stack = []
@nodes = []
@list_depth = -1
@active_styles = []
end
def push(tagname, attrs)
@stack << PendingBlock.from_tag(tagname, attrs, @nodes.dup, @list_depth)
track_block_node(tagname)
end
def push_parent(tagname, attrs)
@list_depth += 1
track_block_node(tagname)
end
def pop_parent(tagname, draftjs)
@nodes.pop
blocks = []
while current.depth >= 0
blocks << @stack.pop
@nodes.pop
end
blocks.reverse_each do |pending_block|
pending_block.flush_to(draftjs)
end
@list_depth -= 1
end
def pop(draftjs)
return if @stack.empty?
return if inside_parent?
if @nodes.last == current.tagname && current.flushable?
flush_to(draftjs)
elsif @stack[-2]
@stack[-2].consume(current)
end
@stack.pop
@nodes.pop
end
def create_pending_entity(tagname, attrs)
current.pending_entities << { tagname: tagname, start: current_character_offset + 1, attrs: attrs }
end
def convert_pending_entities(conversion)
while current.pending_entities.any?
pending_entity = current.pending_entities.pop
range = pending_entity[:start]..current_character_offset
content = current_text_buffer[range]
user_created_entity = conversion.call(pending_entity[:tagname], content, pending_entity[:attrs])
next unless user_created_entity
if content == '' && !user_created_entity[:atomic]
current.text_buffer.append(' ', entity: user_created_entity)
elsif content == '' && user_created_entity[:atomic]
current.text_buffer.append_atomic_entity(user_created_entity)
else
current.text_buffer.apply_entity(range, user_created_entity)
end
end
end
def style_start(tagname)
@active_styles += [DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]]
end
def style_end(tagname)
@active_styles.delete_at(@active_styles.index(DraftjsHtml::HtmlDefaults::HTML_STYLE_TAGS_TO_STYLE[tagname]))
end
def flush_to(draftjs)
current.flush_to(draftjs)
end
def append_text(chars)
current.text_buffer.append(chars, styles: @active_styles) unless chars.empty?
end
private
def current_text_buffer
current.text_buffer.text
end
def current_character_offset
current.character_offset
end
def track_block_node(name)
@nodes << name
end
def inside_parent?
(FromHtml::LIST_PARENT_ELEMENTS & @nodes).any?
end
def current
@stack.last
end
end
end
end