module PandaCms module EditorJs class Renderer attr_reader :content, :options, :custom_renderers, :cache_store def initialize(content, options = {}) @content = content @options = options @custom_renderers = options.delete(:custom_renderers) || {} @cache_store = options.delete(:cache_store) || Rails.cache @validate_html = options.delete(:validate_html) || false end def render return "" if content.nil? || !content.is_a?(Hash) || !content["blocks"] blocks = remove_empty_paragraphs(content["blocks"]) rendered = blocks.map { |block| render_block_with_cache(block) }.join("\n") @validate_html ? validate_html(rendered) : rendered end def section(blocks) "
#{render_blocks(blocks)}
" end def article(blocks, title: nil) content = [] content << "

#{title}

" if title content << render_blocks(blocks) "
#{content.join("\n")}
" end private def render_blocks(blocks) blocks.map { |block| render_block_with_cache(block) }.join("\n") end def validate_html(html) # First check if we have matching numbers of opening and closing tags opening_tags = html.scan(/<([a-z]+)[^>]*>/i) closing_tags = html.scan(/<\/([a-z]+)>/i) # Early return if tag counts don't match return "" unless opening_tags.length == closing_tags.length # Check tag order and nesting stack = [] tag_pattern = /<\/?([a-z]+)[^>]*>/i position = 0 while (match = html[position..].match(tag_pattern)) tag_name = match[1].downcase is_closing = match[0].start_with?("]*>/i) closing_tags = value.scan(/<\/([a-z]+)>/i) opening_tags.length != closing_tags.length || opening_tags.map(&:first) != closing_tags.map(&:first) end end end end end