module DraftjsHtml
# This class manages the depth and nesting of the myriad HTML tags generated by DraftjsHtml::ToHtml.
# It is intended to be a private implementation detail.
class HtmlDepth # :nodoc:
BLOCK_TYPE_TO_HTML_WRAPPER = {
'code-block' => 'pre',
'ordered-list-item' => 'ol',
'unordered-list-item' => 'ul',
}.freeze
def initialize(body)
@current_depth = 0
@body = body
@previous_parents = [body.parent]
@nesting_roots = [body.parent.name]
end
def apply(block)
return unless nesting_root_changed?(block) || depth_changed?(block)
if deepening?(block)
deepen(block, desired_depth_change: block.depth - @current_depth)
elsif rising?(block) && still_nested?(block)
rise(times: @current_depth - block.depth)
elsif rising?(block)
rise(times: @current_depth - block.depth)
pop_parent
elsif still_nested?(block)
push_parent(block)
elsif nested?
pop_parent
end
@current_depth = block.depth
end
private
def deepen(block, desired_depth_change: 0)
if inside_valid_nesting_root?
set_previous_li_as_parent(block)
else
create_valid_nesting_root(block)
end
(desired_depth_change - 1).times do
create_valid_nesting_root(block)
end
push_parent(block)
end
def push_parent(block)
tagname = BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
node = create_child(tagname)
@previous_parents << @body.parent
@body.parent = node
end
def rise(times:)
times.times do
begin
pop_parent
end while @body.parent.name != @nesting_roots.last
@nesting_roots.pop
end
end
def pop_parent
@body.parent = @previous_parents.pop unless @previous_parents.empty?
end
def create_child(tagname)
@body.parent.add_child(@body.doc.create_element(tagname))
end
def nested?
@body.parent.name != 'body'
end
def still_nested?(block)
BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
end
def depth_changed?(block)
block.depth != @current_depth
end
def nesting_root_changed?(block)
@body.parent.name != BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
end
def rising?(block)
@current_depth > block.depth
end
def deepening?(block)
@current_depth < block.depth
end
def create_valid_nesting_root(block)
parent_tagname = BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
node = create_child(parent_tagname)
@previous_parents << node
@nesting_roots << parent_tagname
@body.parent = node
list_item = create_child('li')
@body.parent = list_item
end
def set_previous_li_as_parent(block)
tagname = BLOCK_TYPE_TO_HTML_WRAPPER[block.type]
@previous_parents << @body.parent
@nesting_roots << tagname
@body.parent = @body.parent.last_element_child
end
def inside_valid_nesting_root?
BLOCK_TYPE_TO_HTML_WRAPPER.values.include?(@body.parent.name)
end
end
end