require 'rexml/document'
require 'rubygems'
require 'syntax'
require 'syntax/convertors/html'
class Maruku
include REXML
# Render as an HTML fragment (no head, just the content of BODY). (returns a string)
def to_html
div = Element.new 'dummy'
children_to_html.each do |e|
div << e
end
# render footnotes
if @doc.meta[:footnotes_used]
div << render_footnotes
end
# REXML Bug? if indent!=-1 whitespace is not respected for 'pre' elements
# containing code.
xml =""
div.write_children(xml,indent=-1,transitive=false,ie_hack=true)
xml
end
# Render to a complete HTML document (returns a string)
def to_html_document
doc = to_html_document_tree
xml = ""
# REXML Bug? if indent!=-1 whitespace is not respected for 'pre' elements
# containing code.
doc.write(xml,indent=-1,transitive=false,ie_hack=true);
xhtml10strict = "
\n"
xhtml10strict + xml
end
# Render to a complete HTML document (returns a REXML document tree)
def to_html_document_tree
doc = Document.new(nil,{:respect_whitespace =>:all})
# doc << XMLDecl.new
root = Element.new('html', doc)
root.add_namespace('http://www.w3.org/1999/xhtml')
lang = @meta[:lang] || 'en'
root.attributes['lang'] = lang
root.attributes['xml:lang'] = lang
head = Element.new 'head', root
# Create title element
doc_title = @meta[:title] || @meta[:subject] || ""
title = Element.new 'title'
title << Text.new(doc_title)
head << title
css = @meta[:css]
if css
#
link = Element.new 'link'
link.attributes['type'] = 'text/css'
link.attributes['rel'] = 'stylesheet'
link.attributes['href'] = css
head << link
end
body = Element.new 'body'
children_to_html.each do |e|
body << e
end
# render footnotes
if @doc.meta[:footnotes_used]
body << render_footnotes
end
root << head
root << body
doc
end
def render_footnotes
div = Element.new 'div'
div.attributes['class'] = 'footnotes'
div << Element.new('hr')
ol = Element.new 'ol'
@doc.meta[:footnotes_used].each_with_index do |fid, i| num = i+1
f = @footnotes[fid]
if f
li = f.wrap_as_element('li')
li.attributes['id'] = "fn:#{num}"
a = Element.new 'a'
a.attributes['href'] = "#fnref:#{num}"
a.attributes['rev'] = 'footnote'
a<< Text.new('↩', true, nil, true)
li.children.last << a
ol << li
else
$stderr.puts "Could not find footnote '#{fid}'"
end
end
div << ol
div
end
end
class String
# A string is rendered into HTML by creating
# a REXML::Text node. REXML takes care of all the encoding.
def to_html
REXML::Text.new(self)
end
end
class MDElement
def to_html_hrule; Element.new 'hr' end
def to_html_linebreak; Element.new 'br' end
# renders children as html and wraps into an element of given name
#
# Sets 'id' if meta is set
def wrap_as_element(name)
m = create_html_element name
children_to_html.each do |e| m << e; end
m
end
def create_html_element(name)
m = Element.new name
if @meta[:id] then m.attributes['id'] = @meta[:id].to_s end
if @meta[:style] then m.attributes['style'] = @meta[:style].to_s end
if @meta[:class] then m.attributes['class'] = @meta[:class].to_s end
m
end
def to_html_paragraph; wrap_as_element('p') end
def to_html_ul; wrap_as_element('ul') end
def to_html_ol; wrap_as_element('ol') end
def to_html_li; wrap_as_element('li') end
def to_html_li_span; wrap_as_element('li') end
def to_html_quote; wrap_as_element('blockquote') end
def to_html_strong; wrap_as_element('strong') end
def to_html_emphasis; wrap_as_element('em') end
def to_html_header; wrap_as_element "h#{@meta[:level]}" end
def source2html(source)
source = source.gsub(/&/,'&')
source = Text.normalize(source)
Text.new(source, true, nil, false )
end
def to_html_code;
source = self.meta[:raw_code]
lang = self.meta[:lang] || @doc.meta[:code_lang]
lang = 'xml' if lang=='html'
use_syntax = @doc.meta[:html_use_syntax]
element =
if use_syntax && lang
convertor = Syntax::Convertors::HTML.for_syntax lang
html = convertor.convert( source )
show_spaces = get_setting(:code_show_spaces)
if show_spaces
s.gsub!(/\t/,'»'+' '*3)
s.gsub!(/ /,'¬')
end
# puts "html: #{html}"
pre = Document.new(html, {:respect_whitespace =>:all}).root
pre.attributes['class'] = lang
# puts "After: #{pre}"
pre
else
pre = Element.new 'pre'
s = source
s = s.gsub(/&/,'&')
s = Text.normalize(s)
show_spaces = get_setting(:code_show_spaces)
if show_spaces
s.gsub!(/\t/,'»'+' '*3)
s.gsub!(/ /,'¬')
end
text = Text.new(s, true, nil, false )
pre << text
pre
end
color = get_setting(:code_background_color,DEFAULT_CODE_COLOR)
if color
element.attributes['style'] = "background-color: #{color};"
end
element
end
def to_html_inline_code;
pre = Element.new 'tt'
source = self.meta[:raw_code]
pre << source2html(source)
color = get_setting(:code_background_color, DEFAULT_CODE_COLOR)
if color
pre.attributes['style'] = "background-color: #{color};"
end
pre
end
def to_html_immediate_link
a = Element.new 'a'
url = @meta[:url]
text = url
text = text.gsub(/^mailto:/,'') # don't show mailto
a << Text.new(text)
a.attributes['href'] = url
a
end
def to_html_link
a = wrap_as_element 'a'
id = @meta[:ref_id]
ref = @doc.refs[id]
if not ref
$stderr.puts "Could not find id = '#{id}'"
else
url = ref[:url]
title = ref[:title]
a.attributes['href'] = url
a.attributes['title'] = title if title
end
a
end
##### Email address
def obfuscate(s)
res = ''
s.each_byte do |char|
res += "%03d;" % char
end
res
end
def to_html_email_address
email = @meta[:email]
a = Element.new 'a'
#a.attributes['href'] = Text.new("mailto:"+obfuscate(email),false,nil,true)
#a.attributes.add Attribute.new('href',Text.new(
#"mailto:"+obfuscate(email),false,nil,true))
# Sorry, for the moment it doesn't work
a.attributes['href'] = "mailto:#{email}"
a << Text.new(obfuscate(email),false,nil,true)
a
end
##### Images
def to_html_image
a = Element.new 'img'
id = @meta[:ref_id]
ref = @doc.refs[id]
if not ref
$stderr.puts "Could not find id = '#{id}'"
else
url = ref[:url]
a.attributes['src'] = url
# puts ref.inspect
[:title, :class, :style].each do |s|
if ref[s] then
a.attributes[s.to_s] = ref[s]
end
end
end
a
end
def to_html_raw_html
if @meta[:parsed_html]
return @meta[:parsed_html].root
else # invalid
raw_html = @meta[:raw_html]
# Creates red box with offending HTML
$stderr.puts "Malformed HTML: #{raw_html}"
div = Element.new('pre')
div.attributes['style'] = 'border: solid 3px red; background-color: pink'
div.attributes['class'] = 'markdown-html-error'
div << Text.new("HTML parse error: \n#{raw_html}", true)
return div
end
end
def to_html_abbreviation
abbr = Element.new 'abbr'
abbr << Text.new(children[0])
abbr.attributes['title'] = self.meta[:title] if self.meta[:title]
abbr
end
def to_html_footnote_reference
id = @meta[:footnote_id]
# save the order of used footnotes
order = (@doc.meta[:footnotes_used] ||= [])
# take next number
order << id
num = order.size;
sup = Element.new 'sup'
sup.attributes['id'] = "fnref:#{num}"
a = Element.new 'a'
a << Text.new(num.to_s)
a.attributes['href'] = "\#fn:#{num}"
a.attributes['rel'] = 'footnote'
sup << a
sup
end
## Definition lists ###
def to_html_definition_list
wrap_as_element('dl')
end
def to_html_definition
children_to_html
end
def to_html_definition_term; wrap_as_element('dt') end
def to_html_definition_data; wrap_as_element('dd') end
## Table ###
def to_html_table
align = @meta[:align]
num_columns = align.size
head = @children.slice(0, num_columns)
rows = []
i = num_columns
while i<@children.size
rows << @children.slice(i, num_columns)
i+=num_columns
end
table = create_html_element 'table'
thead = Element.new 'thead'
tr = Element.new 'tr'
array_to_html(head).each do |x| tr<