Class Texy::Html::WellForm
In: lib/texy/html_well_form.rb
Parent: Object

This module ensures that output will be well-formed html (no crossed tags, no opening tags without corresponding closing tags, etc…)

Methods

process  

Constants

AUTO_CLOSE = { 'tbody' => {'thead' => true, 'tbody' => true, 'tfoot' => true, 'colgoup' => true}, 'colgroup' => {'thead' => true, 'tbody' => true, 'tfoot' => true, 'colgoup' => true}, 'dd' => {'dt' => true, 'dd' => true}, 'dt' => {'dt' => true, 'dd' => true}, 'li' => {'li' => true}, 'option' => {'option' => true}, 'p' => { 'address' => true, 'applet' => true, 'blockquote' => true, 'center' => true, 'dir' => true, 'div' => true, 'dl' => true, 'fieldset' => true, 'form' => true, 'h1' => true, 'h2' => true, 'h3' => true, 'h4' => true, 'h5' => true, 'h6' => true, 'hr' => true, 'isindex' => true, 'menu' => true, 'object' => true, 'ol' => true, 'p' => true, 'pre' => true, 'table' => true, 'ul' => true

Public Instance methods

Fix misplaced tags.

For example, turn this

    <strong><em> ... </strong> ... </em>

into this

    <strong><em> ... </em></strong><em> ... </em>

[Source]

# File lib/texy/html_well_form.rb, line 74
            def process(text)
                tag_stack = []

                # <div><strong>hello</div><div>world</strong>

                text.gsub!(/<(\/?)([a-z_:][a-z0-9._:-]*?)(|\s.*?)(\/?)>()/im) do
                    m_closing, m_tag, m_attr, m_empty = !$1.empty?, $2, $3, !$4.empty?

                    if Texy::Html::EMPTY[m_tag] || m_empty
                        next m_closing ? '' : "<#{m_tag}#{m_attr}/>"
                    end

                    replacement = ''

                    if m_closing # closing tag
                        index = tag_stack.size - 1

                        tag_stack.reverse.each do |pair|
                            replacement += "</#{pair[:tag]}>"
                            break if pair[:tag] == m_tag
                            index -= 1
                        end

                        next '' if index < 0

                        if Texy::Html::BLOCK[m_tag]
                            tag_stack.slice!(index..-1)
                        else
                            tag_stack.delete_at(index)
                            tag_stack[index..-1].each do |pair|
                                replacement += "<#{pair[:tag]}#{pair[:attr]}>"
                            end
                        end
                    else # opening
                        while true
                            break unless pair = tag_stack.last
                            break unless AUTO_CLOSE[pair[:tag]] && AUTO_CLOSE[pair[:tag]][m_tag]

                            tag_stack.pop
                            replacement += "</#{pair[:tag]}>"
                        end

                        tag_stack << {:tag => m_tag, :attr => m_attr}
                        replacement += "<#{m_tag}#{m_attr}>"
                    end

                    replacement
                end



                tag_stack.reverse.each do |pair|
                    text += "</#{pair[:tag]}>"
                end

                text
            end

[Validate]