# -*- coding: utf-8 -*- # #-- # Copyright (C) 2009-2010 Thomas Leitner # # This file is part of kramdown. # # kramdown is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . #++ # require 'kramdown/parser/html' module Kramdown module Parser class Kramdown include Kramdown::Parser::Html::Parser def handle_kramdown_html_tag(el, closed) parse_type = if @tree.type != :html_element || @tree.options[:parse_type] != :raw (@doc.options[:parse_block_html] ? HTML_PARSE_AS[el.value] : :raw) else :raw end if val = html_parse_type(el.attr.delete('markdown')) parse_type = (val == :default ? HTML_PARSE_AS[el.value] : val) end @src.scan(/[ \t]*\n/) if parse_type == :block el.options[:parse_type] = parse_type if !closed if parse_type == :block end_tag_found = parse_blocks(el) if !end_tag_found warning("Found no end tag for '#{el.value}' - auto-closing it") end elsif parse_type == :span curpos = @src.pos if result = @src.scan_until(/(?=<\/#{el.value}\s*>)/m) add_text(extract_string(curpos...@src.pos, @src), el) @src.scan(HTML_TAG_CLOSE_RE) else add_text(@src.scan(/.*/m), el) warning("Found no end tag for '#{el.value}' - auto-closing it") end else parse_raw_html(el, &method(:handle_kramdown_html_tag)) end @src.scan(/[ \t]*\n/) unless (@tree.type == :html_element && @tree.options[:parse_type] == :raw) end end # Return the HTML parse type defined by the string +val+, i.e. raw when "0", default parsing # (return value +nil+) when "1", span parsing when "span" and block parsing when "block". If # +val+ is nil, then the default parsing mode is used. def html_parse_type(val) case val when "0" then :raw when "1" then :default when "span" then :span when "block" then :block when NilClass then nil else warning("Invalid markdown attribute val '#{val}', using default") nil end end HTML_BLOCK_START = /^#{OPT_SPACE}<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/ # Parse the HTML at the current position as block level HTML. def parse_block_html if result = @src.scan(HTML_COMMENT_RE) @tree.children << Element.new(:xml_comment, result, nil, :category => :block) @src.scan(/[ \t]*\n/) true elsif result = @src.scan(HTML_INSTRUCTION_RE) @tree.children << Element.new(:xml_pi, result, nil, :category => :block) @src.scan(/[ \t]*\n/) true else if result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1]) @src.pos += @src.matched_size handle_html_start_tag(&method(:handle_kramdown_html_tag)) Kramdown::Parser::Html::ElementConverter.new(@doc).process(@tree.children.last) if @doc.options[:html_to_native] true elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1]) name = @src[1] if @tree.type == :html_element && @tree.value == name @src.pos += @src.matched_size throw :stop_block_parsing, :found else false end else false end end end define_parser(:block_html, HTML_BLOCK_START) HTML_SPAN_START = /<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/ # Parse the HTML at the current position as span level HTML. def parse_span_html if result = @src.scan(HTML_COMMENT_RE) @tree.children << Element.new(:xml_comment, result, nil, :category => :span) elsif result = @src.scan(HTML_INSTRUCTION_RE) @tree.children << Element.new(:xml_pi, result, nil, :category => :span) elsif result = @src.scan(HTML_TAG_CLOSE_RE) warning("Found invalidly used HTML closing tag for '#{@src[1]}'") add_text(result) elsif result = @src.scan(HTML_TAG_RE) if HTML_BLOCK_ELEMENTS.include?(@src[1]) warning("Found block HTML tag '#{@src[1]}' in span level text") add_text(result) return end reset_pos = @src.pos attrs = Utils::OrderedHash.new @src[2].scan(HTML_ATTRIBUTE_RE).each {|name,sep,val| attrs[name] = val.gsub(/\n+/, ' ')} do_parsing = (HTML_PARSE_AS_RAW.include?(@src[1]) || @tree.options[:parse_type] == :raw ? false : @doc.options[:parse_span_html]) if val = html_parse_type(attrs.delete('markdown')) if val == :block warning("Cannot use block level parsing in span level HTML tag - using default mode") elsif val == :span do_parsing = true elsif val == :default do_parsing = !HTML_PARSE_AS_RAW.include?(@src[1]) elsif val == :raw do_parsing = false end end el = Element.new(:html_element, @src[1], attrs, :category => :span, :parse_type => (do_parsing ? :span : :raw)) @tree.children << el stop_re = /<\/#{Regexp.escape(@src[1])}\s*>/ if !@src[4] && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value) warning("The HTML tag '#{el.value}' cannot have any content - auto-closing it") elsif !@src[4] if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html])) @src.scan(stop_re) else warning("Found no end tag for '#{el.value}' - auto-closing it") add_text(@src.scan(/.*/m), el) end end Kramdown::Parser::Html::ElementConverter.new(@doc).process(el) if @doc.options[:html_to_native] else add_text(@src.scan(/./)) end end define_parser(:span_html, HTML_SPAN_START, '<') end end end