Moved vim-markdown-preview to submodule
This commit is contained in:
parent
a7daefcb86
commit
9cb89b4179
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -31,3 +31,6 @@
|
|||||||
[submodule "vim/bundle/tomtom-tcomment_vim"]
|
[submodule "vim/bundle/tomtom-tcomment_vim"]
|
||||||
path = vim/bundle/tomtom-tcomment_vim
|
path = vim/bundle/tomtom-tcomment_vim
|
||||||
url = https://github.com/tomtom/tcomment_vim.git
|
url = https://github.com/tomtom/tcomment_vim.git
|
||||||
|
[submodule "vim/bundle/nelstrom-vim-markdown-preview"]
|
||||||
|
path = vim/bundle/nelstrom-vim-markdown-preview
|
||||||
|
url = https://github.com/nelstrom/vim-markdown-preview
|
||||||
|
@ -145,6 +145,7 @@ Included vim plugins
|
|||||||
* vim-ruby-conque - helpers to run ruby,rspec,rake within ConqueTerm - use \rr (ruby), \ss (rspec), \ll (rspec line), \RR (rake)
|
* vim-ruby-conque - helpers to run ruby,rspec,rake within ConqueTerm - use \rr (ruby), \ss (rspec), \ll (rspec line), \RR (rake)
|
||||||
* AnsiEsc - inteprets ansi color codes inside log files. great for looking at Rails logs
|
* AnsiEsc - inteprets ansi color codes inside log files. great for looking at Rails logs
|
||||||
* ruby-debug-ide - not quite working for me, but maybe it will for you. supposedly a graphical debugger you can step through
|
* ruby-debug-ide - not quite working for me, but maybe it will for you. supposedly a graphical debugger you can step through
|
||||||
|
* tComment - gcc to comment a line, gcp to comment blocks, nuff said
|
||||||
|
|
||||||
Adding your own vim plugins
|
Adding your own vim plugins
|
||||||
---
|
---
|
||||||
|
1
vim/bundle/nelstrom-vim-markdown-preview
Submodule
1
vim/bundle/nelstrom-vim-markdown-preview
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 98844ac2d201fc4e9cfa0a43dfdba6f8dd39a181
|
@ -1,44 +0,0 @@
|
|||||||
body div#content {
|
|
||||||
margin : 0 auto;
|
|
||||||
width : 920px;
|
|
||||||
background-color : #f8f8f8;
|
|
||||||
padding : .7em;
|
|
||||||
font-size : 13.34px;
|
|
||||||
font-family : verdana, sans-serif;
|
|
||||||
border : 1px #E0E0E0 solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#content h2, body div#content h3, body div#content h4 {
|
|
||||||
padding-top : 10px;
|
|
||||||
border-top : 4px solid #E0E0E0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#content pre {
|
|
||||||
padding : 5px;
|
|
||||||
border-style : solid;
|
|
||||||
border-width : 1px;
|
|
||||||
border-color : #E0E0E0;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#content pre code {
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
border : none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#content code {
|
|
||||||
font-family : courier, fixed;
|
|
||||||
display : inline-block;
|
|
||||||
padding : 0px 2px 0px 2px;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
border : 1px #E0E0E0 solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
body h4#title {
|
|
||||||
font-family : verdana, sans-serif;
|
|
||||||
display : block;
|
|
||||||
margin : 0 auto;
|
|
||||||
width : 920px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
kramdown - fast, pure-Ruby Markdown-superset converter
|
|
||||||
Copyright (C) 2009 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
|
|
||||||
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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
Some test cases and the benchmark files are based on test cases from
|
|
||||||
the MDTest test suite:
|
|
||||||
|
|
||||||
MDTest
|
|
||||||
Copyright (c) 2007 Michel Fortin
|
|
||||||
<http://www.michelf.com/>
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
# All the code in this file is backported from Ruby 1.8.7 sothat kramdown works under 1.8.5
|
|
||||||
|
|
||||||
if RUBY_VERSION == '1.8.5'
|
|
||||||
require 'rexml/parsers/baseparser'
|
|
||||||
module REXML
|
|
||||||
module Parsers
|
|
||||||
class BaseParser
|
|
||||||
UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,42 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# == Converter Module
|
|
||||||
#
|
|
||||||
# This module contains all available converters, i.e. classes that take a document and convert the
|
|
||||||
# document tree to a specific output format, normally a string. For example, the Html module
|
|
||||||
# converts the document tree into HTML.
|
|
||||||
#
|
|
||||||
# Converters use the Base class for common functionality (like applying a template to the output)-
|
|
||||||
# see its API documentation for how to create a converter class.
|
|
||||||
module Converter
|
|
||||||
|
|
||||||
autoload :Base, 'kramdown/converter/base'
|
|
||||||
autoload :Html, 'kramdown/converter/html'
|
|
||||||
autoload :Latex, 'kramdown/converter/latex'
|
|
||||||
autoload :Kramdown, 'kramdown/converter/kramdown'
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,111 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'erb'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Converter
|
|
||||||
|
|
||||||
# == Base class for converters
|
|
||||||
#
|
|
||||||
# This class serves as base class for all converters. It provides methods that can/should be
|
|
||||||
# used by all converters (like #generate_id) as well as common functionality that is
|
|
||||||
# automatically applied to the result (for example, embedding the output into a template).
|
|
||||||
#
|
|
||||||
# == Implementing a converter
|
|
||||||
#
|
|
||||||
# Implementing a new converter is rather easy: just create a new sub class from this class and
|
|
||||||
# put it in the Kramdown::Converter module (the latter is only needed if auto-detection should
|
|
||||||
# work properly). Then you need to implement the #convert(tree) method which takes a document
|
|
||||||
# tree and should return the converted output.
|
|
||||||
#
|
|
||||||
# The document instance is automatically set as @doc in Base#initialize. Furthermore, the
|
|
||||||
# document instance provides a hash called `conversion_infos` that is also automatically cleared
|
|
||||||
# and can be used to store information about the conversion process.
|
|
||||||
#
|
|
||||||
# The actual transformation of the document tree can be done in any way. However, writing one
|
|
||||||
# method per tree element type is a straight forward way to do it - this is how the Html and
|
|
||||||
# Latex converters do the transformation.
|
|
||||||
class Base
|
|
||||||
|
|
||||||
# Initialize the converter with the given Kramdown document +doc+.
|
|
||||||
def initialize(doc)
|
|
||||||
@doc = doc
|
|
||||||
@doc.conversion_infos.clear
|
|
||||||
end
|
|
||||||
private_class_method(:new, :allocate)
|
|
||||||
|
|
||||||
# Convert the Kramdown document +doc+ to the output format implemented by a subclass.
|
|
||||||
#
|
|
||||||
# Initializes a new instance of the calling class and then calls the #convert method that must
|
|
||||||
# be implemented by each subclass. If the +template+ option is specified and non-empty, the
|
|
||||||
# result is rendered into the specified template.
|
|
||||||
def self.convert(doc)
|
|
||||||
result = new(doc).convert(doc.tree)
|
|
||||||
result = apply_template(doc, result) if !doc.options[:template].empty?
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
# Apply the template specified in the +doc+ options, using +body+ as the body string.
|
|
||||||
def self.apply_template(doc, body)
|
|
||||||
erb = ERB.new(get_template(doc.options[:template]))
|
|
||||||
obj = Object.new
|
|
||||||
obj.instance_variable_set(:@doc, doc)
|
|
||||||
obj.instance_variable_set(:@body, body)
|
|
||||||
erb.result(obj.instance_eval{binding})
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the template specified by +template+.
|
|
||||||
def self.get_template(template)
|
|
||||||
format_ext = '.' + self.name.split(/::/).last.downcase
|
|
||||||
shipped = File.join(::Kramdown.data_dir, template + format_ext)
|
|
||||||
if File.exist?(template)
|
|
||||||
File.read(template)
|
|
||||||
elsif File.exist?(template + format_ext)
|
|
||||||
File.read(template + format_ext)
|
|
||||||
elsif File.exist?(shipped)
|
|
||||||
File.read(shipped)
|
|
||||||
else
|
|
||||||
raise "The specified template file #{template} does not exist"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Generate an unique alpha-numeric ID from the the string +str+ for use as header ID.
|
|
||||||
def generate_id(str)
|
|
||||||
gen_id = str.gsub(/[^a-zA-Z0-9 -]/, '').gsub(/^[^a-zA-Z]*/, '').gsub(' ', '-').downcase
|
|
||||||
gen_id = 'section' if gen_id.length == 0
|
|
||||||
@used_ids ||= {}
|
|
||||||
if @used_ids.has_key?(gen_id)
|
|
||||||
gen_id += '-' + (@used_ids[gen_id] += 1).to_s
|
|
||||||
else
|
|
||||||
@used_ids[gen_id] = 0
|
|
||||||
end
|
|
||||||
@doc.options[:auto_id_prefix] + gen_id
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,391 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'rexml/parsers/baseparser'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Converter
|
|
||||||
|
|
||||||
# Converts a Kramdown::Document to HTML.
|
|
||||||
class Html < Base
|
|
||||||
|
|
||||||
include ::Kramdown::Utils::HTML
|
|
||||||
|
|
||||||
# DEPRECATED: use #html_attributes
|
|
||||||
def options_for_element(el)
|
|
||||||
warn("DEPRECATION WARNING: this method will be deprecated in the next release, use #html_attributes instead")
|
|
||||||
html_attributes(el)
|
|
||||||
end
|
|
||||||
|
|
||||||
# :stopdoc:
|
|
||||||
|
|
||||||
# Defines the amount of indentation used when nesting HTML tags.
|
|
||||||
INDENTATION = 2
|
|
||||||
|
|
||||||
begin
|
|
||||||
require 'coderay'
|
|
||||||
|
|
||||||
# Highlighting via coderay is available if this constant is +true+.
|
|
||||||
HIGHLIGHTING_AVAILABLE = true
|
|
||||||
rescue LoadError => e
|
|
||||||
HIGHLIGHTING_AVAILABLE = false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initialize the HTML converter with the given Kramdown document +doc+.
|
|
||||||
def initialize(doc)
|
|
||||||
super
|
|
||||||
@footnote_counter = @footnote_start = @doc.options[:footnote_nr]
|
|
||||||
@footnotes = []
|
|
||||||
@toc = []
|
|
||||||
@toc_code = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert(el, indent = -INDENTATION, opts = {})
|
|
||||||
send("convert_#{el.type}", el, indent, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def inner(el, indent, opts)
|
|
||||||
result = ''
|
|
||||||
indent += INDENTATION
|
|
||||||
el.children.each do |inner_el|
|
|
||||||
result << send("convert_#{inner_el.type}", inner_el, indent, opts)
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blank(el, indent, opts)
|
|
||||||
"\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_text(el, indent, opts)
|
|
||||||
escape_html(el.value, :text)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_p(el, indent, opts)
|
|
||||||
if el.options[:transparent]
|
|
||||||
"#{inner(el, indent, opts)}"
|
|
||||||
else
|
|
||||||
"#{' '*indent}<p#{html_attributes(el)}>#{inner(el, indent, opts)}</p>\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_codeblock(el, indent, opts)
|
|
||||||
if el.options[:attr] && el.options[:attr]['lang'] && HIGHLIGHTING_AVAILABLE
|
|
||||||
el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
|
|
||||||
opts = {:wrap => @doc.options[:coderay_wrap], :line_numbers => @doc.options[:coderay_line_numbers],
|
|
||||||
:line_number_start => @doc.options[:coderay_line_number_start], :tab_width => @doc.options[:coderay_tab_width],
|
|
||||||
:bold_every => @doc.options[:coderay_bold_every], :css => @doc.options[:coderay_css]}
|
|
||||||
result = CodeRay.scan(el.value, el.options[:attr].delete('lang').to_sym).html(opts).chomp + "\n"
|
|
||||||
"#{' '*indent}<div#{html_attributes(el)}>#{result}#{' '*indent}</div>\n"
|
|
||||||
else
|
|
||||||
result = escape_html(el.value)
|
|
||||||
if el.options[:attr] && el.options[:attr].has_key?('class') && el.options[:attr]['class'] =~ /\bshow-whitespaces\b/
|
|
||||||
result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m|
|
|
||||||
suffix = ($1 ? '-l' : ($2 ? '-r' : ''))
|
|
||||||
m.scan(/./).map do |c|
|
|
||||||
case c
|
|
||||||
when "\t" then "<span class=\"ws-tab#{suffix}\">\t</span>"
|
|
||||||
when " " then "<span class=\"ws-space#{suffix}\">⋅</span>"
|
|
||||||
end
|
|
||||||
end.join('')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"#{' '*indent}<pre#{html_attributes(el)}><code>#{result}#{result =~ /\n\Z/ ? '' : "\n"}</code></pre>\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blockquote(el, indent, opts)
|
|
||||||
"#{' '*indent}<blockquote#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</blockquote>\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_header(el, indent, opts)
|
|
||||||
el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
|
|
||||||
if @doc.options[:auto_ids] && !(el.options[:attr] && el.options[:attr]['id'])
|
|
||||||
(el.options[:attr] ||= {})['id'] = generate_id(el.options[:raw_text])
|
|
||||||
end
|
|
||||||
@toc << [el.options[:level], el.options[:attr]['id'], el.children] if el.options[:attr] && el.options[:attr]['id'] && within_toc_depth?(el)
|
|
||||||
"#{' '*indent}<h#{el.options[:level]}#{html_attributes(el)}>#{inner(el, indent, opts)}</h#{el.options[:level]}>\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def within_toc_depth?(el)
|
|
||||||
@doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth]
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_hr(el, indent, opts)
|
|
||||||
"#{' '*indent}<hr />\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_ul(el, indent, opts)
|
|
||||||
if !@toc_code && (el.options[:ial][:refs].include?('toc') rescue nil) && (el.type == :ul || el.type == :ol)
|
|
||||||
@toc_code = [el.type, el.options[:attr], (0..128).to_a.map{|a| rand(36).to_s(36)}.join]
|
|
||||||
@toc_code.last
|
|
||||||
else
|
|
||||||
"#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</#{el.type}>\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_ol :convert_ul
|
|
||||||
alias :convert_dl :convert_ul
|
|
||||||
|
|
||||||
def convert_li(el, indent, opts)
|
|
||||||
output = ' '*indent << "<#{el.type}" << html_attributes(el) << ">"
|
|
||||||
res = inner(el, indent, opts)
|
|
||||||
if el.children.empty? || (el.children.first.type == :p && el.children.first.options[:transparent])
|
|
||||||
output << res << (res =~ /\n\Z/ ? ' '*indent : '')
|
|
||||||
else
|
|
||||||
output << "\n" << res << ' '*indent
|
|
||||||
end
|
|
||||||
output << "</#{el.type}>\n"
|
|
||||||
end
|
|
||||||
alias :convert_dd :convert_li
|
|
||||||
|
|
||||||
def convert_dt(el, indent, opts)
|
|
||||||
"#{' '*indent}<dt#{html_attributes(el)}>#{inner(el, indent, opts)}</dt>\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
HTML_TAGS_WITH_BODY=['div', 'script']
|
|
||||||
|
|
||||||
def convert_html_element(el, indent, opts)
|
|
||||||
res = inner(el, indent, opts)
|
|
||||||
if el.options[:category] == :span
|
|
||||||
"<#{el.value}#{html_attributes(el)}" << (!res.empty? ? ">#{res}</#{el.value}>" : " />")
|
|
||||||
else
|
|
||||||
output = ''
|
|
||||||
output << ' '*indent if !el.options[:parent_is_raw]
|
|
||||||
output << "<#{el.value}#{html_attributes(el)}"
|
|
||||||
if !res.empty? && el.options[:parse_type] != :block
|
|
||||||
output << ">#{res}</#{el.value}>"
|
|
||||||
elsif !res.empty?
|
|
||||||
output << ">\n#{res}" << ' '*indent << "</#{el.value}>"
|
|
||||||
elsif HTML_TAGS_WITH_BODY.include?(el.value)
|
|
||||||
output << "></#{el.value}>"
|
|
||||||
else
|
|
||||||
output << " />"
|
|
||||||
end
|
|
||||||
output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw]
|
|
||||||
output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_xml_comment(el, indent, opts)
|
|
||||||
if el.options[:category] == :block && !el.options[:parent_is_raw]
|
|
||||||
' '*indent + el.value + "\n"
|
|
||||||
else
|
|
||||||
el.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_xml_pi :convert_xml_comment
|
|
||||||
alias :convert_html_doctype :convert_xml_comment
|
|
||||||
|
|
||||||
def convert_table(el, indent, opts)
|
|
||||||
if el.options[:alignment].all? {|a| a == :default}
|
|
||||||
alignment = ''
|
|
||||||
else
|
|
||||||
alignment = el.options[:alignment].map do |a|
|
|
||||||
"#{' '*(indent + INDENTATION)}" + (a == :default ? "<col />" : "<col align=\"#{a}\" />") + "\n"
|
|
||||||
end.join('')
|
|
||||||
end
|
|
||||||
"#{' '*indent}<table#{html_attributes(el)}>\n#{alignment}#{inner(el, indent, opts)}#{' '*indent}</table>\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_thead(el, indent, opts)
|
|
||||||
"#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</#{el.type}>\n"
|
|
||||||
end
|
|
||||||
alias :convert_tbody :convert_thead
|
|
||||||
alias :convert_tfoot :convert_thead
|
|
||||||
alias :convert_tr :convert_thead
|
|
||||||
|
|
||||||
def convert_td(el, indent, opts)
|
|
||||||
res = inner(el, indent, opts)
|
|
||||||
"#{' '*indent}<#{el.type}#{html_attributes(el)}>#{res.empty? ? " " : res}</#{el.type}>\n"
|
|
||||||
end
|
|
||||||
alias :convert_th :convert_td
|
|
||||||
|
|
||||||
def convert_comment(el, indent, opts)
|
|
||||||
if el.options[:category] == :block
|
|
||||||
"#{' '*indent}<!-- #{el.value} -->\n"
|
|
||||||
else
|
|
||||||
"<!-- #{el.value} -->"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_br(el, indent, opts)
|
|
||||||
"<br />"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_a(el, indent, opts)
|
|
||||||
do_obfuscation = el.options[:attr]['href'] =~ /^mailto:/
|
|
||||||
if do_obfuscation
|
|
||||||
el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
|
|
||||||
href = obfuscate(el.options[:attr]['href'].sub(/^mailto:/, ''))
|
|
||||||
mailto = obfuscate('mailto')
|
|
||||||
el.options[:attr]['href'] = "#{mailto}:#{href}"
|
|
||||||
end
|
|
||||||
res = inner(el, indent, opts)
|
|
||||||
res = obfuscate(res) if do_obfuscation
|
|
||||||
"<a#{html_attributes(el)}>#{res}</a>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_img(el, indent, opts)
|
|
||||||
"<img#{html_attributes(el)} />"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_codespan(el, indent, opts)
|
|
||||||
"<code#{html_attributes(el)}>#{escape_html(el.value)}</code>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_footnote(el, indent, opts)
|
|
||||||
number = @footnote_counter
|
|
||||||
@footnote_counter += 1
|
|
||||||
@footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]]
|
|
||||||
"<sup id=\"fnref:#{el.options[:name]}\"><a href=\"#fn:#{el.options[:name]}\" rel=\"footnote\">#{number}</a></sup>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_raw(el, indent, opts)
|
|
||||||
el.value + (el.options[:category] == :block ? "\n" : '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_em(el, indent, opts)
|
|
||||||
"<#{el.type}#{html_attributes(el)}>#{inner(el, indent, opts)}</#{el.type}>"
|
|
||||||
end
|
|
||||||
alias :convert_strong :convert_em
|
|
||||||
|
|
||||||
def convert_entity(el, indent, opts)
|
|
||||||
entity_to_str(el.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
TYPOGRAPHIC_SYMS = {
|
|
||||||
:mdash => [::Kramdown::Utils::Entities.entity('mdash')],
|
|
||||||
:ndash => [::Kramdown::Utils::Entities.entity('ndash')],
|
|
||||||
:hellip => [::Kramdown::Utils::Entities.entity('hellip')],
|
|
||||||
:laquo_space => [::Kramdown::Utils::Entities.entity('laquo'), ::Kramdown::Utils::Entities.entity('nbsp')],
|
|
||||||
:raquo_space => [::Kramdown::Utils::Entities.entity('nbsp'), ::Kramdown::Utils::Entities.entity('raquo')],
|
|
||||||
:laquo => [::Kramdown::Utils::Entities.entity('laquo')],
|
|
||||||
:raquo => [::Kramdown::Utils::Entities.entity('raquo')]
|
|
||||||
}
|
|
||||||
def convert_typographic_sym(el, indent, opts)
|
|
||||||
TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('')
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_smart_quote(el, indent, opts)
|
|
||||||
entity_to_str(::Kramdown::Utils::Entities.entity(el.value.to_s))
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_math(el, indent, opts)
|
|
||||||
el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
|
|
||||||
el.options[:attr] ||= {}
|
|
||||||
el.options[:attr]['class'] ||= ''
|
|
||||||
el.options[:attr]['class'] += (el.options[:attr]['class'].empty? ? '' : ' ') + 'math'
|
|
||||||
type = 'span'
|
|
||||||
type = 'div' if el.options[:category] == :block
|
|
||||||
"<#{type}#{html_attributes(el)}>#{escape_html(el.value)}</#{type}>#{type == 'div' ? "\n" : ''}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_abbreviation(el, indent, opts)
|
|
||||||
title = @doc.parse_infos[:abbrev_defs][el.value]
|
|
||||||
title = nil if title.empty?
|
|
||||||
"<abbr#{title ? " title=\"#{title}\"" : ''}>#{el.value}</abbr>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_root(el, indent, opts)
|
|
||||||
result = inner(el, indent, opts)
|
|
||||||
result << footnote_content
|
|
||||||
if @toc_code
|
|
||||||
toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {})
|
|
||||||
text = if toc_tree.children.size > 0
|
|
||||||
convert(toc_tree, 0)
|
|
||||||
else
|
|
||||||
''
|
|
||||||
end
|
|
||||||
result.sub!(/#{@toc_code.last}/, text)
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_toc_tree(toc, type, attr)
|
|
||||||
sections = Element.new(type, nil, {:attr => {'id' => 'markdown-toc'}.merge(attr)})
|
|
||||||
stack = []
|
|
||||||
toc.each do |level, id, children|
|
|
||||||
li = Element.new(:li, nil, {:level => level})
|
|
||||||
li.children << Element.new(:p, nil, {:transparent => true})
|
|
||||||
a = Element.new(:a, nil, {:attr => {:href => "##{id}"}})
|
|
||||||
a.children += children
|
|
||||||
li.children.last.children << a
|
|
||||||
li.children << Element.new(type)
|
|
||||||
|
|
||||||
success = false
|
|
||||||
while !success
|
|
||||||
if stack.empty?
|
|
||||||
sections.children << li
|
|
||||||
stack << li
|
|
||||||
success = true
|
|
||||||
elsif stack.last.options[:level] < li.options[:level]
|
|
||||||
stack.last.children.last.children << li
|
|
||||||
stack << li
|
|
||||||
success = true
|
|
||||||
else
|
|
||||||
item = stack.pop
|
|
||||||
item.children.pop unless item.children.last.children.size > 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while !stack.empty?
|
|
||||||
item = stack.pop
|
|
||||||
item.children.pop unless item.children.last.children.size > 0
|
|
||||||
end
|
|
||||||
sections
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper method for obfuscating the +text+ by using HTML entities.
|
|
||||||
def obfuscate(text)
|
|
||||||
result = ""
|
|
||||||
text.each_byte do |b|
|
|
||||||
result += (b > 128 ? b.chr : "&#%03d;" % b)
|
|
||||||
end
|
|
||||||
result.force_encoding(text.encoding) if RUBY_VERSION >= '1.9'
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a HTML list with the footnote content for the used footnotes.
|
|
||||||
def footnote_content
|
|
||||||
ol = Element.new(:ol)
|
|
||||||
ol.options[:attr] = {'start' => @footnote_start} if @footnote_start != 1
|
|
||||||
@footnotes.each do |name, data|
|
|
||||||
li = Element.new(:li, nil, {:attr => {:id => "fn:#{name}"}, :first_is_block => true})
|
|
||||||
li.children = Marshal.load(Marshal.dump(data[:content].children)) #TODO: probably remove this!!!!
|
|
||||||
ol.children << li
|
|
||||||
|
|
||||||
ref = Element.new(:raw, "<a href=\"#fnref:#{name}\" rev=\"footnote\">↩</a>")
|
|
||||||
if li.children.last.type == :p
|
|
||||||
para = li.children.last
|
|
||||||
else
|
|
||||||
li.children << (para = Element.new(:p))
|
|
||||||
end
|
|
||||||
para.children << ref
|
|
||||||
end
|
|
||||||
(ol.children.empty? ? '' : "<div class=\"footnotes\">\n#{convert(ol, 2)}</div>\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,398 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'rexml/parsers/baseparser'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Converter
|
|
||||||
|
|
||||||
# Converts a Kramdown::Document to the kramdown format.
|
|
||||||
class Kramdown < Base
|
|
||||||
|
|
||||||
# :stopdoc:
|
|
||||||
|
|
||||||
include ::Kramdown::Utils::HTML
|
|
||||||
|
|
||||||
def initialize(doc)
|
|
||||||
super
|
|
||||||
@linkrefs = []
|
|
||||||
@footnotes = []
|
|
||||||
@abbrevs = []
|
|
||||||
@stack = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert(el, opts = {})
|
|
||||||
res = send("convert_#{el.type}", el, opts)
|
|
||||||
if el.type != :html_element && el.type != :li && el.type != :dd && (ial = ial_for_element(el))
|
|
||||||
res << ial
|
|
||||||
res << "\n\n" if el.options[:category] == :block
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def inner(el, opts = {})
|
|
||||||
@stack.push([el, opts])
|
|
||||||
result = ''
|
|
||||||
el.children.each_with_index do |inner_el, index|
|
|
||||||
options = opts.dup
|
|
||||||
#p [index, inner_el]
|
|
||||||
options[:index] = index
|
|
||||||
options[:prev] = (index == 0 ? nil : el.children[index-1])
|
|
||||||
options[:next] = (index == el.children.length - 1 ? nil : el.children[index+1])
|
|
||||||
result << convert(inner_el, options)
|
|
||||||
end
|
|
||||||
@stack.pop
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blank(el, opts)
|
|
||||||
"\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
ESCAPED_CHAR_RE = /(\$\$|[\\*_`\[\]\{\}"'])|^[ ]{0,3}(:)/
|
|
||||||
|
|
||||||
def convert_text(el, opts)
|
|
||||||
if opts[:raw_text]
|
|
||||||
el.value
|
|
||||||
else
|
|
||||||
nl = (el.value =~ /\n$/)
|
|
||||||
el.value.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" } + (nl ? "\n" : '')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_p(el, opts)
|
|
||||||
res = inner(el, opts).strip.gsub(/\A(?:([#|])|(\d+)\.|([+-]\s))/) do
|
|
||||||
$1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\."
|
|
||||||
end + "\n"
|
|
||||||
if opts[:next] && opts[:next].type == :p && !ial_for_element(el)
|
|
||||||
res += "\n"
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
CODEBLOCK_PREV_EL = [:ul, :ol, :dl, :codeblock]
|
|
||||||
|
|
||||||
def convert_codeblock(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << "^\n" if opts[:prev] && ((CODEBLOCK_PREV_EL.include?(opts[:prev].type) && !ial_for_element(opts[:prev])) ||
|
|
||||||
(opts[:prev].type == :blank &&
|
|
||||||
opts[:index]-2 >= 0 &&
|
|
||||||
(tmp = @stack.last.first.children[opts[:index]-2]) &&
|
|
||||||
CODEBLOCK_PREV_EL.include?(tmp.type) && !ial_for_element(tmp)))
|
|
||||||
res << el.value.split(/\n/).map {|l| l.empty? ? " " : " #{l}"}.join("\n") + "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blockquote(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << "\n" if opts[:prev] && opts[:prev].type == :blockquote
|
|
||||||
res << inner(el, opts).chomp.split(/\n/).map {|l| "> #{l}"}.join("\n") << "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_header(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << "\n" if opts[:prev] && opts[:prev].type != :blank
|
|
||||||
res << "#{'#' * el.options[:level]} #{inner(el, opts)}"
|
|
||||||
res << " {##{el.options[:attr]['id']}}" if el.options[:attr] && el.options[:attr]['id']
|
|
||||||
res << "\n" if opts[:next] && opts[:next].type != :blank
|
|
||||||
res << "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_hr(el, opts)
|
|
||||||
"* * *\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_ul(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << "\n" if opts[:prev] && (opts[:prev].type == :p && !opts[:prev].options[:transparent])
|
|
||||||
res << "^\n" if opts[:prev] && ((opts[:prev].type == el.type && !ial_for_element(opts[:prev])) ||
|
|
||||||
(opts[:prev].type == :blank && opts[:index]-2 >= 0 &&
|
|
||||||
(tmp = @stack.last.first.children[opts[:index]-2]) &&
|
|
||||||
tmp.type == el.type && !ial_for_element(tmp)))
|
|
||||||
res + inner(el, opts).sub(/\n+\Z/, "\n")
|
|
||||||
end
|
|
||||||
alias :convert_ol :convert_ul
|
|
||||||
alias :convert_dl :convert_ul
|
|
||||||
|
|
||||||
def convert_li(el, opts)
|
|
||||||
sym, width = if @stack.last.first.type == :ul
|
|
||||||
['* ', el.children.first.type == :codeblock ? 4 : 2]
|
|
||||||
else
|
|
||||||
["#{opts[:index] + 1}.".ljust(4), 4]
|
|
||||||
end
|
|
||||||
if ial = ial_for_element(el)
|
|
||||||
sym += ial + " "
|
|
||||||
end
|
|
||||||
|
|
||||||
first, *last = inner(el, opts).chomp.split(/\n/)
|
|
||||||
last = last.map {|l| " "*width + l}.join("\n")
|
|
||||||
last = last.empty? ? "\n" : "\n#{last}\n"
|
|
||||||
if el.children.first.type == :p && !el.children.first.options[:transparent]
|
|
||||||
res = "#{sym}#{first}\n#{last}"
|
|
||||||
res << "^\n" if el.children.size == 1 && @stack.last.first.children.last == el &&
|
|
||||||
(@stack.last.first.children.any? {|c| c.children.first.type != :p} || @stack.last.first.children.size == 1)
|
|
||||||
res
|
|
||||||
elsif el.children.first.type == :codeblock
|
|
||||||
"#{sym}\n #{first}#{last}"
|
|
||||||
else
|
|
||||||
"#{sym}#{first}#{last}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_dd(el, opts)
|
|
||||||
sym, width = ": ", (el.children.first.type == :codeblock ? 4 : 2)
|
|
||||||
if ial = ial_for_element(el)
|
|
||||||
sym += ial + " "
|
|
||||||
end
|
|
||||||
|
|
||||||
first, *last = inner(el, opts).chomp.split(/\n/)
|
|
||||||
last = last.map {|l| " "*width + l}.join("\n")
|
|
||||||
text = first + (last.empty? ? '' : "\n" + last)
|
|
||||||
if el.children.first.type == :p && !el.children.first.options[:transparent]
|
|
||||||
"\n#{sym}#{text}\n"
|
|
||||||
elsif el.children.first.type == :codeblock
|
|
||||||
"#{sym}\n #{text}\n"
|
|
||||||
else
|
|
||||||
"#{sym}#{text}\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_dt(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << inner(el, opts) << "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
HTML_TAGS_WITH_BODY=['div', 'script']
|
|
||||||
|
|
||||||
def convert_html_element(el, opts)
|
|
||||||
markdown_attr = el.options[:category] == :block && el.children.any? do |c|
|
|
||||||
c.type != :html_element && (c.type != :p || !c.options[:transparent]) && c.options[:category] == :block
|
|
||||||
end
|
|
||||||
opts[:force_raw_text] = true if %w{script pre code}.include?(el.value)
|
|
||||||
opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || (el.options[:category] != :span && !markdown_attr)
|
|
||||||
opts[:block_raw_text] = true if el.options[:category] == :block && opts[:raw_text]
|
|
||||||
res = inner(el, opts)
|
|
||||||
if el.options[:category] == :span
|
|
||||||
"<#{el.value}#{html_attributes(el)}" << (!res.empty? ? ">#{res}</#{el.value}>" : " />")
|
|
||||||
else
|
|
||||||
output = ''
|
|
||||||
output << "<#{el.value}#{html_attributes(el)}"
|
|
||||||
output << " markdown=\"1\"" if markdown_attr
|
|
||||||
if !res.empty? && el.options[:parse_type] != :block
|
|
||||||
output << ">#{res}</#{el.value}>"
|
|
||||||
elsif !res.empty?
|
|
||||||
output << ">\n#{res}" << "</#{el.value}>"
|
|
||||||
elsif HTML_TAGS_WITH_BODY.include?(el.value)
|
|
||||||
output << "></#{el.value}>"
|
|
||||||
else
|
|
||||||
output << " />"
|
|
||||||
end
|
|
||||||
output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw]
|
|
||||||
output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_xml_comment(el, opts)
|
|
||||||
if el.options[:category] == :block && !el.options[:parent_is_raw]
|
|
||||||
el.value + "\n"
|
|
||||||
else
|
|
||||||
el.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_xml_pi :convert_xml_comment
|
|
||||||
alias :convert_html_doctype :convert_xml_comment
|
|
||||||
|
|
||||||
def convert_table(el, opts)
|
|
||||||
opts[:alignment] = el.options[:alignment]
|
|
||||||
inner(el, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_thead(el, opts)
|
|
||||||
rows = inner(el, opts)
|
|
||||||
if opts[:alignment].all? {|a| a == :default}
|
|
||||||
"#{rows}|" + "-"*10 + "\n"
|
|
||||||
else
|
|
||||||
"#{rows}| " + opts[:alignment].map do |a|
|
|
||||||
case a
|
|
||||||
when :left then ":-"
|
|
||||||
when :right then "-:"
|
|
||||||
when :center then ":-:"
|
|
||||||
when :default then "-"
|
|
||||||
end
|
|
||||||
end.join(' ') + "\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tbody(el, opts)
|
|
||||||
res = ''
|
|
||||||
res << inner(el, opts)
|
|
||||||
res << '|' << '-'*10 << "\n" if opts[:next] && opts[:next].type == :tbody
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tfoot(el, opts)
|
|
||||||
"|" + "="*10 + "\n#{inner(el, opts)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tr(el, opts)
|
|
||||||
"| " + el.children.map {|c| convert(c, opts)}.join(" | ") + " |\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_td(el, opts)
|
|
||||||
inner(el, opts).gsub(/\|/, '\\|')
|
|
||||||
end
|
|
||||||
alias :convert_th :convert_td
|
|
||||||
|
|
||||||
def convert_comment(el, opts)
|
|
||||||
if el.options[:category] == :block
|
|
||||||
"{::comment}\n#{el.value}\n{:/}\n"
|
|
||||||
else
|
|
||||||
"{::comment}#{el.value}{:/}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_br(el, opts)
|
|
||||||
" \n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_a(el, opts)
|
|
||||||
if el.options[:attr]['href'].empty?
|
|
||||||
"[#{inner(el, opts)}]()"
|
|
||||||
else
|
|
||||||
@linkrefs << el
|
|
||||||
"[#{inner(el, opts)}][#{@linkrefs.size}]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_img(el, opts)
|
|
||||||
title = (el.options[:attr]['title'] ? ' "' + el.options[:attr]['title'].gsub(/"/, """) + '"' : '')
|
|
||||||
"![#{el.options[:attr]['alt']}](<#{el.options[:attr]['src']}>#{title})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_codespan(el, opts)
|
|
||||||
delim = (el.value.scan(/`+/).max || '') + '`'
|
|
||||||
"#{delim}#{' ' if delim.size > 1}#{el.value}#{' ' if delim.size > 1}#{delim}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_footnote(el, opts)
|
|
||||||
@footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]]
|
|
||||||
"[^#{el.options[:name]}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_raw(el, opts)
|
|
||||||
if @stack.last.first.type == :html_element
|
|
||||||
el.value
|
|
||||||
elsif el.options[:category] == :block
|
|
||||||
"{::nomarkdown}\n#{el.value}\n{:/}\n"
|
|
||||||
else
|
|
||||||
"{::nomarkdown}#{el.value}{:/}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_em(el, opts)
|
|
||||||
"*#{inner(el, opts)}*"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_strong(el, opts)
|
|
||||||
"**#{inner(el, opts)}**"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_entity(el, opts)
|
|
||||||
entity_to_str(el.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
TYPOGRAPHIC_SYMS = {
|
|
||||||
:mdash => '---', :ndash => '--', :hellip => '...',
|
|
||||||
:laquo_space => '<< ', :raquo_space => ' >>',
|
|
||||||
:laquo => '<<', :raquo => '>>'
|
|
||||||
}
|
|
||||||
def convert_typographic_sym(el, opts)
|
|
||||||
TYPOGRAPHIC_SYMS[el.value]
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_smart_quote(el, opts)
|
|
||||||
el.value.to_s =~ /[rl]dquo/ ? "\"" : "'"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_math(el, opts)
|
|
||||||
(@stack.last.first.type == :p && opts[:prev].nil? ? "\\" : '') + "$$#{el.value}$$" + (el.options[:category] == :block ? "\n" : '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_abbreviation(el, opts)
|
|
||||||
el.value
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_root(el, opts)
|
|
||||||
res = inner(el, opts)
|
|
||||||
res << create_link_defs
|
|
||||||
res << create_footnote_defs
|
|
||||||
res << create_abbrev_defs
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_link_defs
|
|
||||||
res = ''
|
|
||||||
res << "\n\n" if @linkrefs.size > 0
|
|
||||||
@linkrefs.each_with_index do |el, i|
|
|
||||||
link = (el.type == :a ? el.options[:attr]['href'] : el.options[:attr]['src'])
|
|
||||||
link = "<#{link}>" if link =~ / /
|
|
||||||
title = el.options[:attr]['title']
|
|
||||||
res << "[#{i+1}]: #{link} #{title ? '"' + title.gsub(/"/, """) + '"' : ''}\n"
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_footnote_defs
|
|
||||||
res = ''
|
|
||||||
res = "\n" if @footnotes.size > 0
|
|
||||||
@footnotes.each do |name, data|
|
|
||||||
res << "\n[^#{name}]:\n"
|
|
||||||
res << inner(data[:content]).chomp.split(/\n/).map {|l| " #{l}"}.join("\n")
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_abbrev_defs
|
|
||||||
return '' unless @doc.parse_infos[:abbrev_defs]
|
|
||||||
res = ''
|
|
||||||
@doc.parse_infos[:abbrev_defs].each do |name, text|
|
|
||||||
res << "*[#{name}]: #{text}\n"
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the IAL containing the attributes of the element +el+.
|
|
||||||
def ial_for_element(el)
|
|
||||||
res = (el.options[:attr] || {}).map do |k,v|
|
|
||||||
next if [:img, :a].include?(el.type) && ['href', 'src', 'alt', 'title'].include?(k)
|
|
||||||
next if el.type == :header && k == 'id'
|
|
||||||
v.nil? ? '' : " #{k}=\"#{v.to_s}\""
|
|
||||||
end.compact.sort.join('')
|
|
||||||
res = "toc" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
|
|
||||||
(el.options[:ial][:refs].include?('toc') rescue nil)
|
|
||||||
res.strip.empty? ? nil : "{:#{res}}"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,553 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'set'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Converter
|
|
||||||
|
|
||||||
# Converts a Kramdown::Document to LaTeX. This converter uses ideas from other Markdown-to-LaTeX
|
|
||||||
# converters like Pandoc and Maruku.
|
|
||||||
class Latex < Base
|
|
||||||
|
|
||||||
# :stopdoc:
|
|
||||||
|
|
||||||
# Initialize the LaTeX converter with the given Kramdown document +doc+.
|
|
||||||
def initialize(doc)
|
|
||||||
super
|
|
||||||
#TODO: set the footnote counter at the beginning of the document
|
|
||||||
@doc.options[:footnote_nr]
|
|
||||||
@doc.conversion_infos[:packages] = Set.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert(el, opts = {})
|
|
||||||
send("convert_#{el.type}", el, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def inner(el, opts)
|
|
||||||
result = ''
|
|
||||||
el.children.each do |inner_el|
|
|
||||||
result << send("convert_#{inner_el.type}", inner_el, opts)
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_root(el, opts)
|
|
||||||
inner(el, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blank(el, opts)
|
|
||||||
""
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_text(el, opts)
|
|
||||||
escape(el.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_p(el, opts)
|
|
||||||
"#{inner(el, opts)}\n\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_codeblock(el, opts)
|
|
||||||
show_whitespace = el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bshow-whitespaces\b/
|
|
||||||
lang = el.options[:attr] && el.options[:attr]['lang']
|
|
||||||
if show_whitespace || lang
|
|
||||||
result = "\\lstset{showspaces=%s,showtabs=%s}\n" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
|
|
||||||
result += "\\lstset{language=#{lang}}\n" if lang
|
|
||||||
result += "\\lstset{basicstyle=\\ttfamily\\footnotesize}\\lstset{columns=fixed,frame=tlbr}\n"
|
|
||||||
"#{result}\\begin{lstlisting}#{attribute_list(el)}\n#{el.value}\n\\end{lstlisting}\n"
|
|
||||||
else
|
|
||||||
"\\begin{verbatim}#{el.value}\\end{verbatim}\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def latex_environment(type, el, text)
|
|
||||||
"\\begin{#{type}}#{attribute_list(el)}\n#{text}\n\\end{#{type}}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_blockquote(el, opts)
|
|
||||||
latex_environment('quote', el, inner(el, opts))
|
|
||||||
end
|
|
||||||
|
|
||||||
HEADER_TYPES = {
|
|
||||||
1 => 'section',
|
|
||||||
2 => 'subsection',
|
|
||||||
3 => 'subsubsection',
|
|
||||||
4 => 'paragraph',
|
|
||||||
5 => 'subparagraph',
|
|
||||||
6 => 'subparagraph'
|
|
||||||
}
|
|
||||||
def convert_header(el, opts)
|
|
||||||
type = HEADER_TYPES[el.options[:level]]
|
|
||||||
if ((el.options[:attr] && (id = el.options[:attr]['id'])) ||
|
|
||||||
(@doc.options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) &&
|
|
||||||
(@doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth])
|
|
||||||
"\\hypertarget{#{id}}{}\\#{type}{#{inner(el, opts)}}\\label{#{id}}\n\n"
|
|
||||||
else
|
|
||||||
"\\#{type}*{#{inner(el, opts)}}\n\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_hr(el, opts)
|
|
||||||
"\\begin{center}#{attribute_list(el)}\n\\rule{3in}{0.4pt}\n\\end{center}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_ul(el, opts)
|
|
||||||
if !@doc.conversion_infos[:has_toc] && (el.options[:ial][:refs].include?('toc') rescue nil)
|
|
||||||
@doc.conversion_infos[:has_toc] = true
|
|
||||||
'\tableofcontents'
|
|
||||||
else
|
|
||||||
latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_ol :convert_ul
|
|
||||||
|
|
||||||
def convert_dl(el, opts)
|
|
||||||
latex_environment('description', el, inner(el, opts))
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_li(el, opts)
|
|
||||||
"\\item #{inner(el, opts).sub(/\n+\Z/, '')}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_dt(el, opts)
|
|
||||||
"\\item[#{inner(el, opts)}] "
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_dd(el, opts)
|
|
||||||
"#{inner(el, opts)}\n\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_html_element(el, opts)
|
|
||||||
if el.value == 'i'
|
|
||||||
"\\emph{#{inner(el, opts)}}"
|
|
||||||
elsif el.value == 'b'
|
|
||||||
"\\emph{#{inner(el, opts)}}"
|
|
||||||
else
|
|
||||||
@doc.warnings << "Can't convert HTML element"
|
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_xml_comment(el, opts)
|
|
||||||
el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_xml_pi(el, opts)
|
|
||||||
@doc.warnings << "Can't convert XML PI/HTML document type"
|
|
||||||
''
|
|
||||||
end
|
|
||||||
alias :convert_html_doctype :convert_xml_pi
|
|
||||||
|
|
||||||
TABLE_ALIGNMENT_CHAR = {:default => 'l', :left => 'l', :center => 'c', :right => 'r'}
|
|
||||||
|
|
||||||
def convert_table(el, opts)
|
|
||||||
align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|')
|
|
||||||
"\\begin{tabular}{|#{align}|}#{attribute_list(el)}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}\n\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_thead(el, opts)
|
|
||||||
"#{inner(el, opts)}\\hline\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tbody(el, opts)
|
|
||||||
inner(el, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tfoot(el, opts)
|
|
||||||
"\\hline \\hline \n#{inner(el, opts)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_tr(el, opts)
|
|
||||||
el.children.map {|c| send("convert_#{c.type}", c, opts)}.join(' & ') + "\\\\\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_td(el, opts)
|
|
||||||
inner(el, opts)
|
|
||||||
end
|
|
||||||
alias :convert_th :convert_td
|
|
||||||
|
|
||||||
def convert_comment(el, opts)
|
|
||||||
el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_br(el, opts)
|
|
||||||
"\\newline\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_a(el, opts)
|
|
||||||
url = el.options[:attr]['href']
|
|
||||||
if url =~ /^#/
|
|
||||||
"\\hyperlink{#{url[1..-1]}}{#{inner(el, opts)}}"
|
|
||||||
else
|
|
||||||
"\\href{#{url}}{#{inner(el, opts)}}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_img(el, opts)
|
|
||||||
if el.options[:attr]['src'] =~ /^(https?|ftps?):\/\//
|
|
||||||
@doc.warnings << "Cannot include non-local image"
|
|
||||||
''
|
|
||||||
elsif !el.options[:attr]['src'].empty?
|
|
||||||
@doc.conversion_infos[:packages] << 'graphicx'
|
|
||||||
"\\includegraphics{#{el.options[:attr]['src']}}"
|
|
||||||
else
|
|
||||||
@doc.warnings << "Cannot include image with empty path"
|
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_codespan(el, opts)
|
|
||||||
"{\\tt #{escape(el.value)}}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_footnote(el, opts)
|
|
||||||
@doc.conversion_infos[:packages] << 'fancyvrb'
|
|
||||||
"\\footnote{#{inner(@doc.parse_infos[:footnotes][el.options[:name]][:content], opts)}}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_raw(el, opts)
|
|
||||||
escape(el.value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_em(el, opts)
|
|
||||||
"\\emph{#{inner(el, opts)}}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_strong(el, opts)
|
|
||||||
"\\textbf{#{inner(el, opts)}}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Inspired by Maruku: entity conversion table based on the one from htmltolatex
|
|
||||||
# (http://sourceforge.net/projects/htmltolatex/), with some small adjustments/additions
|
|
||||||
ENTITY_CONV_TABLE = {
|
|
||||||
913 => ['$A$'],
|
|
||||||
914 => ['$B$'],
|
|
||||||
915 => ['$\Gamma$'],
|
|
||||||
916 => ['$\Delta$'],
|
|
||||||
917 => ['$E$'],
|
|
||||||
918 => ['$Z$'],
|
|
||||||
919 => ['$H$'],
|
|
||||||
920 => ['$\Theta$'],
|
|
||||||
921 => ['$I$'],
|
|
||||||
922 => ['$K$'],
|
|
||||||
923 => ['$\Lambda$'],
|
|
||||||
924 => ['$M$'],
|
|
||||||
925 => ['$N$'],
|
|
||||||
926 => ['$\Xi$'],
|
|
||||||
927 => ['$O$'],
|
|
||||||
928 => ['$\Pi$'],
|
|
||||||
929 => ['$P$'],
|
|
||||||
931 => ['$\Sigma$'],
|
|
||||||
932 => ['$T$'],
|
|
||||||
933 => ['$Y$'],
|
|
||||||
934 => ['$\Phi$'],
|
|
||||||
935 => ['$X$'],
|
|
||||||
936 => ['$\Psi$'],
|
|
||||||
937 => ['$\Omega$'],
|
|
||||||
945 => ['$\alpha$'],
|
|
||||||
946 => ['$\beta$'],
|
|
||||||
947 => ['$\gamma$'],
|
|
||||||
948 => ['$\delta$'],
|
|
||||||
949 => ['$\epsilon$'],
|
|
||||||
950 => ['$\zeta$'],
|
|
||||||
951 => ['$\eta$'],
|
|
||||||
952 => ['$\theta$'],
|
|
||||||
953 => ['$\iota$'],
|
|
||||||
954 => ['$\kappa$'],
|
|
||||||
955 => ['$\lambda$'],
|
|
||||||
956 => ['$\mu$'],
|
|
||||||
957 => ['$\nu$'],
|
|
||||||
958 => ['$\xi$'],
|
|
||||||
959 => ['$o$'],
|
|
||||||
960 => ['$\pi$'],
|
|
||||||
961 => ['$\rho$'],
|
|
||||||
963 => ['$\sigma$'],
|
|
||||||
964 => ['$\tau$'],
|
|
||||||
965 => ['$\upsilon$'],
|
|
||||||
966 => ['$\phi$'],
|
|
||||||
967 => ['$\chi$'],
|
|
||||||
968 => ['$\psi$'],
|
|
||||||
969 => ['$\omega$'],
|
|
||||||
962 => ['$\varsigma$'],
|
|
||||||
977 => ['$\vartheta$'],
|
|
||||||
982 => ['$\varpi$'],
|
|
||||||
8230 => ['\ldots'],
|
|
||||||
8242 => ['$\prime$'],
|
|
||||||
8254 => ['-'],
|
|
||||||
8260 => ['/'],
|
|
||||||
8472 => ['$\wp$'],
|
|
||||||
8465 => ['$\Im$'],
|
|
||||||
8476 => ['$\Re$'],
|
|
||||||
8501 => ['$\aleph$'],
|
|
||||||
8226 => ['$\bullet$'],
|
|
||||||
8482 => ['$^{\rm TM}$'],
|
|
||||||
8592 => ['$\leftarrow$'],
|
|
||||||
8594 => ['$\rightarrow$'],
|
|
||||||
8593 => ['$\uparrow$'],
|
|
||||||
8595 => ['$\downarrow$'],
|
|
||||||
8596 => ['$\leftrightarrow$'],
|
|
||||||
8629 => ['$\hookleftarrow$'],
|
|
||||||
8657 => ['$\Uparrow$'],
|
|
||||||
8659 => ['$\Downarrow$'],
|
|
||||||
8656 => ['$\Leftarrow$'],
|
|
||||||
8658 => ['$\Rightarrow$'],
|
|
||||||
8660 => ['$\Leftrightarrow$'],
|
|
||||||
8704 => ['$\forall$'],
|
|
||||||
8706 => ['$\partial$'],
|
|
||||||
8707 => ['$\exists$'],
|
|
||||||
8709 => ['$\emptyset$'],
|
|
||||||
8711 => ['$\nabla$'],
|
|
||||||
8712 => ['$\in$'],
|
|
||||||
8715 => ['$\ni$'],
|
|
||||||
8713 => ['$\notin$'],
|
|
||||||
8721 => ['$\sum$'],
|
|
||||||
8719 => ['$\prod$'],
|
|
||||||
8722 => ['$-$'],
|
|
||||||
8727 => ['$\ast$'],
|
|
||||||
8730 => ['$\surd$'],
|
|
||||||
8733 => ['$\propto$'],
|
|
||||||
8734 => ['$\infty$'],
|
|
||||||
8736 => ['$\angle$'],
|
|
||||||
8743 => ['$\wedge$'],
|
|
||||||
8744 => ['$\vee$'],
|
|
||||||
8745 => ['$\cup$'],
|
|
||||||
8746 => ['$\cap$'],
|
|
||||||
8747 => ['$\int$'],
|
|
||||||
8756 => ['$\therefore$', 'amssymb'],
|
|
||||||
8764 => ['$\sim$'],
|
|
||||||
8776 => ['$\approx$'],
|
|
||||||
8773 => ['$\cong$'],
|
|
||||||
8800 => ['$\neq$'],
|
|
||||||
8801 => ['$\equiv$'],
|
|
||||||
8804 => ['$\leq$'],
|
|
||||||
8805 => ['$\geq$'],
|
|
||||||
8834 => ['$\subset$'],
|
|
||||||
8835 => ['$\supset$'],
|
|
||||||
8838 => ['$\subseteq$'],
|
|
||||||
8839 => ['$\supseteq$'],
|
|
||||||
8836 => ['$\nsubset$', 'amssymb'],
|
|
||||||
8853 => ['$\oplus$'],
|
|
||||||
8855 => ['$\otimes$'],
|
|
||||||
8869 => ['$\perp$'],
|
|
||||||
8901 => ['$\cdot$'],
|
|
||||||
8968 => ['$\rceil$'],
|
|
||||||
8969 => ['$\lceil$'],
|
|
||||||
8970 => ['$\lfloor$'],
|
|
||||||
8971 => ['$\rfloor$'],
|
|
||||||
9001 => ['$\rangle$'],
|
|
||||||
9002 => ['$\langle$'],
|
|
||||||
9674 => ['$\lozenge$', 'amssymb'],
|
|
||||||
9824 => ['$\spadesuit$'],
|
|
||||||
9827 => ['$\clubsuit$'],
|
|
||||||
9829 => ['$\heartsuit$'],
|
|
||||||
9830 => ['$\diamondsuit$'],
|
|
||||||
38 => ['\&'],
|
|
||||||
34 => ['"'],
|
|
||||||
39 => ['\''],
|
|
||||||
169 => ['\copyright'],
|
|
||||||
60 => ['\textless{}'],
|
|
||||||
62 => ['\textgreater{}'],
|
|
||||||
338 => ['\OE'],
|
|
||||||
339 => ['\oe'],
|
|
||||||
352 => ['\v{S}'],
|
|
||||||
353 => ['\v{s}'],
|
|
||||||
376 => ['\"Y'],
|
|
||||||
710 => ['\textasciicircum'],
|
|
||||||
732 => ['\textasciitilde'],
|
|
||||||
8211 => ['--'],
|
|
||||||
8212 => ['---'],
|
|
||||||
8216 => ['`'],
|
|
||||||
8217 => ['\''],
|
|
||||||
8220 => ['``'],
|
|
||||||
8221 => ['\'\''],
|
|
||||||
8224 => ['\dag'],
|
|
||||||
8225 => ['\ddag'],
|
|
||||||
8240 => ['\permil', 'wasysym'],
|
|
||||||
8364 => ['\euro', 'eurosym'],
|
|
||||||
8249 => ['\guilsinglleft'],
|
|
||||||
8250 => ['\guilsinglright'],
|
|
||||||
8218 => ['\quotesinglbase', 'mathcomp'],
|
|
||||||
8222 => ['\quotedblbase', 'mathcomp'],
|
|
||||||
402 => ['\textflorin', 'mathcomp'],
|
|
||||||
381 => ['\v{Z}'],
|
|
||||||
382 => ['\v{z}'],
|
|
||||||
160 => ['\nolinebreak'],
|
|
||||||
161 => ['\textexclamdown'],
|
|
||||||
163 => ['\pounds'],
|
|
||||||
164 => ['\currency', 'wasysym'],
|
|
||||||
165 => ['\textyen', 'textcomp'],
|
|
||||||
166 => ['\brokenvert', 'wasysym'],
|
|
||||||
167 => ['\S'],
|
|
||||||
171 => ['\guillemotleft'],
|
|
||||||
187 => ['\guillemotright'],
|
|
||||||
174 => ['\textregistered'],
|
|
||||||
170 => ['\textordfeminine'],
|
|
||||||
172 => ['$\neg$'],
|
|
||||||
176 => ['$\degree$', 'mathabx'],
|
|
||||||
177 => ['$\pm$'],
|
|
||||||
180 => ['\''],
|
|
||||||
181 => ['$\mu$'],
|
|
||||||
182 => ['\P'],
|
|
||||||
183 => ['$\cdot$'],
|
|
||||||
186 => ['\textordmasculine'],
|
|
||||||
162 => ['\cent', 'wasysym'],
|
|
||||||
185 => ['$^1$'],
|
|
||||||
178 => ['$^2$'],
|
|
||||||
179 => ['$^3$'],
|
|
||||||
189 => ['$\frac{1}{2}$'],
|
|
||||||
188 => ['$\frac{1}{4}$'],
|
|
||||||
190 => ['$\frac{3}{4}'],
|
|
||||||
192 => ['\`A'],
|
|
||||||
193 => ['\\\'A'],
|
|
||||||
194 => ['\^A'],
|
|
||||||
195 => ['\~A'],
|
|
||||||
196 => ['\"A'],
|
|
||||||
197 => ['\AA'],
|
|
||||||
198 => ['\AE'],
|
|
||||||
199 => ['\cC'],
|
|
||||||
200 => ['\`E'],
|
|
||||||
201 => ['\\\'E'],
|
|
||||||
202 => ['\^E'],
|
|
||||||
203 => ['\"E'],
|
|
||||||
204 => ['\`I'],
|
|
||||||
205 => ['\\\'I'],
|
|
||||||
206 => ['\^I'],
|
|
||||||
207 => ['\"I'],
|
|
||||||
208 => ['$\eth$', 'amssymb'],
|
|
||||||
209 => ['\~N'],
|
|
||||||
210 => ['\`O'],
|
|
||||||
211 => ['\\\'O'],
|
|
||||||
212 => ['\^O'],
|
|
||||||
213 => ['\~O'],
|
|
||||||
214 => ['\"O'],
|
|
||||||
215 => ['$\times$'],
|
|
||||||
216 => ['\O'],
|
|
||||||
217 => ['\`U'],
|
|
||||||
218 => ['\\\'U'],
|
|
||||||
219 => ['\^U'],
|
|
||||||
220 => ['\"U'],
|
|
||||||
221 => ['\\\'Y'],
|
|
||||||
222 => ['\Thorn', 'wasysym'],
|
|
||||||
223 => ['\ss'],
|
|
||||||
224 => ['\`a'],
|
|
||||||
225 => ['\\\'a'],
|
|
||||||
226 => ['\^a'],
|
|
||||||
227 => ['\~a'],
|
|
||||||
228 => ['\"a'],
|
|
||||||
229 => ['\aa'],
|
|
||||||
230 => ['\ae'],
|
|
||||||
231 => ['\cc'],
|
|
||||||
232 => ['\`e'],
|
|
||||||
233 => ['\\\'e'],
|
|
||||||
234 => ['\^e'],
|
|
||||||
235 => ['\"e'],
|
|
||||||
236 => ['\`i'],
|
|
||||||
237 => ['\\\'i'],
|
|
||||||
238 => ['\^i'],
|
|
||||||
239 => ['\"i'],
|
|
||||||
240 => ['$\eth$'],
|
|
||||||
241 => ['\~n'],
|
|
||||||
242 => ['\`o'],
|
|
||||||
243 => ['\\\'o'],
|
|
||||||
244 => ['\^o'],
|
|
||||||
245 => ['\~o'],
|
|
||||||
246 => ['\"o'],
|
|
||||||
247 => ['$\divide$'],
|
|
||||||
248 => ['\o'],
|
|
||||||
249 => ['\`u'],
|
|
||||||
250 => ['\\\'u'],
|
|
||||||
251 => ['\^u'],
|
|
||||||
252 => ['\"u'],
|
|
||||||
253 => ['\\\'y'],
|
|
||||||
254 => ['\thorn', 'wasysym'],
|
|
||||||
255 => ['\"y'],
|
|
||||||
}
|
|
||||||
ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k] = v.unshift(v.shift + '{}')}
|
|
||||||
|
|
||||||
def convert_entity(el, opts)
|
|
||||||
text, package = ENTITY_CONV_TABLE[el.value.code_point]
|
|
||||||
if text
|
|
||||||
@doc.conversion_infos[:packages] << package if package
|
|
||||||
text
|
|
||||||
else
|
|
||||||
@doc.warnings << "Couldn't find entity in substitution table!"
|
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
TYPOGRAPHIC_SYMS = {
|
|
||||||
:mdash => '---', :ndash => '--', :hellip => '\ldots{}',
|
|
||||||
:laquo_space => '\guillemotleft{}~', :raquo_space => '~\guillemotright{}',
|
|
||||||
:laquo => '\guillemotleft{}', :raquo => '\guillemotright{}'
|
|
||||||
}
|
|
||||||
def convert_typographic_sym(el, opts)
|
|
||||||
TYPOGRAPHIC_SYMS[el.value]
|
|
||||||
end
|
|
||||||
|
|
||||||
SMART_QUOTE_SYMS = {:lsquo => '`', :rsquo => '\'', :ldquo => '``', :rdquo => '\'\''}
|
|
||||||
def convert_smart_quote(el, opts)
|
|
||||||
SMART_QUOTE_SYMS[el.value]
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_math(el, opts)
|
|
||||||
@doc.conversion_infos[:packages] += %w[amssymb amsmath amsthm amsfonts]
|
|
||||||
if el.options[:category] == :block
|
|
||||||
if el.value =~ /\A\s*\\begin\{/
|
|
||||||
el.value
|
|
||||||
else
|
|
||||||
latex_environment('displaymath', el, el.value)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
"$#{el.value}$"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_abbreviation(el, opts)
|
|
||||||
el.value
|
|
||||||
end
|
|
||||||
|
|
||||||
ESCAPE_MAP = {
|
|
||||||
"^" => "\\^{}",
|
|
||||||
"\\" => "\\textbackslash{}",
|
|
||||||
"~" => "\\ensuremath{\\sim}",
|
|
||||||
"|" => "\\textbar{}",
|
|
||||||
"<" => "\\textless{}",
|
|
||||||
">" => "\\textgreater{}"
|
|
||||||
}.merge(Hash[*("{}$%&_#".scan(/./).map {|c| [c, "\\#{c}"]}.flatten)])
|
|
||||||
ESCAPE_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k})
|
|
||||||
|
|
||||||
def escape(str)
|
|
||||||
str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def attribute_list(el)
|
|
||||||
attrs = (el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
|
|
||||||
attrs = " % #{attrs}" if !attrs.empty?
|
|
||||||
attrs
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,166 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/compatibility'
|
|
||||||
|
|
||||||
require 'kramdown/version'
|
|
||||||
require 'kramdown/error'
|
|
||||||
require 'kramdown/parser'
|
|
||||||
require 'kramdown/converter'
|
|
||||||
require 'kramdown/options'
|
|
||||||
require 'kramdown/utils'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# Return the data directory for kramdown.
|
|
||||||
def self.data_dir
|
|
||||||
unless defined?(@@data_dir)
|
|
||||||
require 'rbconfig'
|
|
||||||
@@data_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'kramdown'))
|
|
||||||
@@data_dir = File.expand_path(File.join(Config::CONFIG["datadir"], "kramdown")) if !File.exists?(@@data_dir)
|
|
||||||
raise "kramdown data directory not found! This is a bug, please report it!" unless File.directory?(@@data_dir)
|
|
||||||
end
|
|
||||||
@@data_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# The main interface to kramdown.
|
|
||||||
#
|
|
||||||
# This class provides a one-stop-shop for using kramdown to convert text into various output
|
|
||||||
# formats. Use it like this:
|
|
||||||
#
|
|
||||||
# require 'kramdown'
|
|
||||||
# doc = Kramdown::Document.new('This *is* some kramdown text')
|
|
||||||
# puts doc.to_html
|
|
||||||
#
|
|
||||||
# The #to_html method is a shortcut for using the Converter::Html class.
|
|
||||||
#
|
|
||||||
# The second argument to the #new method is an options hash for customizing the behaviour of the
|
|
||||||
# used parser and the converter. See Document#new for more information!
|
|
||||||
class Document
|
|
||||||
|
|
||||||
# The element tree of the document. It is immediately available after the #new method has been
|
|
||||||
# called.
|
|
||||||
attr_accessor :tree
|
|
||||||
|
|
||||||
# The options hash which holds the options for parsing/converting the Kramdown document. It is
|
|
||||||
# possible that these values get changed during the parsing phase.
|
|
||||||
attr_reader :options
|
|
||||||
|
|
||||||
# An array of warning messages. It is filled with warnings during the parsing phase (i.e. in
|
|
||||||
# #new) and the conversion phase.
|
|
||||||
attr_reader :warnings
|
|
||||||
|
|
||||||
# Holds needed parse information which is dependent on the used parser, like ALDs, link
|
|
||||||
# definitions and so on. This information may be used by converters afterwards.
|
|
||||||
attr_reader :parse_infos
|
|
||||||
|
|
||||||
# Holds conversion information which is dependent on the used converter. A converter clears this
|
|
||||||
# variable before doing the conversion.
|
|
||||||
attr_reader :conversion_infos
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new Kramdown document from the string +source+ and use the provided +options+. The
|
|
||||||
# options that can be used are defined in the Options module.
|
|
||||||
#
|
|
||||||
# The special options key <tt>:input</tt> can be used to select the parser that should parse the
|
|
||||||
# +source+. It has to be the name of a class in the Kramdown::Parser module. For example, to
|
|
||||||
# select the kramdown parser, one would set the <tt>:input</tt> key to +Kramdown+. If this key
|
|
||||||
# is not set, it defaults to +Kramdown+.
|
|
||||||
#
|
|
||||||
# The +source+ is immediately parsed by the selected parser so that the document tree is
|
|
||||||
# immediately available and the output can be generated.
|
|
||||||
def initialize(source, options = {})
|
|
||||||
@options = Options.merge(options)
|
|
||||||
@warnings = []
|
|
||||||
@parse_infos = {}
|
|
||||||
@parse_infos[:encoding] = source.encoding if RUBY_VERSION >= '1.9'
|
|
||||||
@conversion_infos = {}
|
|
||||||
parser = (options[:input] || 'kramdown').to_s
|
|
||||||
parser = parser[0..0].upcase + parser[1..-1]
|
|
||||||
if Parser.const_defined?(parser)
|
|
||||||
@tree = Parser.const_get(parser).parse(source, self)
|
|
||||||
else
|
|
||||||
raise Kramdown::Error.new("kramdown has no parser to handle the specified input format: #{options[:input]}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if a method is invoked that begins with +to_+ and if so, try to instantiate a converter
|
|
||||||
# class (i.e. a class in the Kramdown::Converter module) and use it for converting the document.
|
|
||||||
#
|
|
||||||
# For example, +to_html+ would instantiate the Kramdown::Converter::Html class.
|
|
||||||
def method_missing(id, *attr, &block)
|
|
||||||
if id.to_s =~ /^to_(\w+)$/
|
|
||||||
Converter.const_get($1[0..0].upcase + $1[1..-1]).convert(self)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect #:nodoc:
|
|
||||||
"<KD:Document: options=#{@options.inspect} tree=#{@tree.inspect} warnings=#{@warnings.inspect}>"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Represents all elements in the parse tree.
|
|
||||||
#
|
|
||||||
# kramdown only uses this one class for representing all available elements in a parse tree
|
|
||||||
# (paragraphs, headers, emphasis, ...). The type of element can be set via the #type accessor.
|
|
||||||
class Element
|
|
||||||
|
|
||||||
# A symbol representing the element type. For example, <tt>:p</tt> or <tt>:blockquote</tt>.
|
|
||||||
attr_accessor :type
|
|
||||||
|
|
||||||
# The value of the element. The interpretation of this field depends on the type of the element.
|
|
||||||
# Many elements don't use this field.
|
|
||||||
attr_accessor :value
|
|
||||||
|
|
||||||
# The options hash for the element. It is used for storing arbitray options as well as the
|
|
||||||
# following special contents:
|
|
||||||
#
|
|
||||||
# - *Attributes* of the element under the <tt>:attr</tt> key
|
|
||||||
# - Category of the element, either <tt>:block</tt> or <tt>:span</tt>, under the
|
|
||||||
# <tt>:category</tt> key. If this key is absent, it can be assumed that the element is in the
|
|
||||||
# <tt>:span</tt> category.
|
|
||||||
attr_accessor :options
|
|
||||||
|
|
||||||
# The child elements of this element.
|
|
||||||
attr_accessor :children
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new Element object of type +type+. The optional parameters +value+ and +options+ can
|
|
||||||
# also be set in this constructor for convenience.
|
|
||||||
def initialize(type, value = nil, options = {})
|
|
||||||
@type, @value, @options = type, value, options
|
|
||||||
@children = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect #:nodoc:
|
|
||||||
"<kd:#{@type}#{@value.nil? ? '' : ' ' + @value.inspect}#{options.empty? ? '' : ' ' + @options.inspect}#{@children.empty? ? '' : ' ' + @children.inspect}>"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
class Error < RuntimeError; end
|
|
||||||
|
|
||||||
end
|
|
@ -1,23 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/document'
|
|
@ -1,283 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# This module defines all options that are used by parsers and/or converters as well as providing
|
|
||||||
# methods to deal with the options.
|
|
||||||
module Options
|
|
||||||
|
|
||||||
# Helper class introducing a boolean type for specifying boolean values (+true+ and +false+) as
|
|
||||||
# option types.
|
|
||||||
class Boolean
|
|
||||||
|
|
||||||
# Return +true+ if +other+ is either +true+ or +false+
|
|
||||||
def self.===(other)
|
|
||||||
FalseClass === other || TrueClass === other
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------
|
|
||||||
# :section: Option definitions
|
|
||||||
#
|
|
||||||
# This sections informs describes the methods that can be used on the Options module.
|
|
||||||
# ----------------------------
|
|
||||||
|
|
||||||
# Contains the definition of an option.
|
|
||||||
Definition = Struct.new(:name, :type, :default, :desc)
|
|
||||||
|
|
||||||
# Allowed option types.
|
|
||||||
ALLOWED_TYPES = [String, Integer, Float, Symbol, Boolean, Array, Object]
|
|
||||||
|
|
||||||
@options = {}
|
|
||||||
|
|
||||||
# Define a new option called +name+ (a Symbol) with the given +type+ (String, Integer, Float,
|
|
||||||
# Symbol, Boolean, Array, Object), default value +default+ and the description +desc+.
|
|
||||||
#
|
|
||||||
# The type 'Object' should only be used if none of the other types suffices because such an
|
|
||||||
# option will be opaque and cannot be used, for example, by CLI command!
|
|
||||||
def self.define(name, type, default, desc)
|
|
||||||
raise ArgumentError, "Option name #{name} is already used" if @options.has_key?(name)
|
|
||||||
raise ArgumentError, "Invalid option type #{type} specified" if !ALLOWED_TYPES.include?(type)
|
|
||||||
raise ArgumentError, "Invalid type for default value" if !(type === default) && !default.nil?
|
|
||||||
@options[name] = Definition.new(name, type, default, desc)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return all option definitions.
|
|
||||||
def self.definitions
|
|
||||||
@options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return +true+ if an option called +name+ is defined.
|
|
||||||
def self.defined?(name)
|
|
||||||
@options.has_key?(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a Hash with the default values for all options.
|
|
||||||
def self.defaults
|
|
||||||
temp = {}
|
|
||||||
@options.each {|n, o| temp[o.name] = o.default}
|
|
||||||
temp
|
|
||||||
end
|
|
||||||
|
|
||||||
# Merge the #defaults Hash with the *parsed* options from the given Hash, i.e. only valid option
|
|
||||||
# names are considered and their value is run through the #parse method.
|
|
||||||
def self.merge(hash)
|
|
||||||
temp = defaults
|
|
||||||
hash.each do |k,v|
|
|
||||||
next unless @options.has_key?(k)
|
|
||||||
temp[k] = parse(k, v)
|
|
||||||
end
|
|
||||||
temp
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse the given value +data+ as if it was a value for the option +name+ and return the parsed
|
|
||||||
# value with the correct type.
|
|
||||||
#
|
|
||||||
# If +data+ already has the correct type, it is just returned. Otherwise it is converted to a
|
|
||||||
# String and then to the correct type.
|
|
||||||
def self.parse(name, data)
|
|
||||||
raise ArgumentError, "No option named #{name} defined" if !@options.has_key?(name)
|
|
||||||
return data if @options[name].type === data
|
|
||||||
data = data.to_s
|
|
||||||
if @options[name].type == String
|
|
||||||
data
|
|
||||||
elsif @options[name].type == Integer
|
|
||||||
Integer(data)
|
|
||||||
elsif @options[name].type == Float
|
|
||||||
Float(data)
|
|
||||||
elsif @options[name].type == Symbol
|
|
||||||
(data.strip.empty? ? nil : data.to_sym)
|
|
||||||
elsif @options[name].type == Boolean
|
|
||||||
data.downcase.strip != 'false' && !data.empty?
|
|
||||||
elsif @options[name].type == Array
|
|
||||||
data.split(/\s+/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ----------------------------
|
|
||||||
# :section: Option Definitions
|
|
||||||
#
|
|
||||||
# This sections contains all option definitions that are used by the included
|
|
||||||
# parsers/converters.
|
|
||||||
# ----------------------------
|
|
||||||
|
|
||||||
define(:template, String, '', <<EOF)
|
|
||||||
The name of an ERB template file that should be used to wrap the output
|
|
||||||
|
|
||||||
This is used to wrap the output in an environment so that the output can
|
|
||||||
be used as a stand-alone document. For example, an HTML template would
|
|
||||||
provide the needed header and body tags so that the whole output is a
|
|
||||||
valid HTML file. If no template is specified, the output will be just
|
|
||||||
the converted text.
|
|
||||||
|
|
||||||
When resolving the template file, the given template name is used first.
|
|
||||||
If such a file is not found, the converter extension is appended. If the
|
|
||||||
file still cannot be found, the templates name is interpreted as a
|
|
||||||
template name that is provided by kramdown (without the converter
|
|
||||||
extension).
|
|
||||||
|
|
||||||
kramdown provides a default template named 'default' for each converter.
|
|
||||||
|
|
||||||
Default: ''
|
|
||||||
Used by: all converters
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:auto_ids, Boolean, true, <<EOF)
|
|
||||||
Use automatic header ID generation
|
|
||||||
|
|
||||||
If this option is `true`, ID values for all headers are automatically
|
|
||||||
generated if no ID is explicitly specified.
|
|
||||||
|
|
||||||
Default: true
|
|
||||||
Used by: HTML/Latex converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:auto_id_prefix, String, '', <<EOF)
|
|
||||||
Prefix used for automatically generated heaer IDs
|
|
||||||
|
|
||||||
This option can be used to set a prefix for the automatically generated
|
|
||||||
header IDs so that there is no conflict when rendering multiple kramdown
|
|
||||||
documents into one output file separately. The prefix should only
|
|
||||||
contain characters that are valid in an ID!
|
|
||||||
|
|
||||||
Default: ''
|
|
||||||
Used by: HTML/Latex converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:parse_block_html, Boolean, false, <<EOF)
|
|
||||||
Process kramdown syntax in block HTML tags
|
|
||||||
|
|
||||||
If this option is `true`, the kramdown parser processes the content of
|
|
||||||
block HTML tags as text containing block level elements. Since this is
|
|
||||||
not wanted normally, the default is `false`. It is normally better to
|
|
||||||
selectively enable kramdown processing via the markdown attribute.
|
|
||||||
|
|
||||||
Default: false
|
|
||||||
Used by: kramdown parser
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:parse_span_html, Boolean, true, <<EOF)
|
|
||||||
Process kramdown syntax in span HTML tags
|
|
||||||
|
|
||||||
If this option is `true`, the kramdown parser processes the content of
|
|
||||||
span HTML tags as text containing span level elements.
|
|
||||||
|
|
||||||
Default: true
|
|
||||||
Used by: kramdown parser
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:html_to_native, Boolean, false, <<EOF)
|
|
||||||
Convert HTML elements to native elements
|
|
||||||
|
|
||||||
If this option is `true`, the parser converts HTML elements to native
|
|
||||||
elements. For example, when parsing `<em>hallo</em>` the emphasis tag
|
|
||||||
would normally be converted to an `:html` element with tag type `:em`.
|
|
||||||
If `html_to_native` is `true`, then the emphasis would be converted to a
|
|
||||||
native `:em` element.
|
|
||||||
|
|
||||||
This is useful for converters that cannot deal with HTML elements.
|
|
||||||
|
|
||||||
Default: false
|
|
||||||
Used by: kramdown parser
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:footnote_nr, Integer, 1, <<EOF)
|
|
||||||
The number of the first footnote
|
|
||||||
|
|
||||||
This option can be used to specify the number that is used for the first
|
|
||||||
footnote.
|
|
||||||
|
|
||||||
Default: 1
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_wrap, Symbol, :div, <<EOF)
|
|
||||||
Defines how the highlighted code should be wrapped
|
|
||||||
|
|
||||||
The possible values are :span, :div or nil.
|
|
||||||
|
|
||||||
Default: :div
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_line_numbers, Symbol, :inline, <<EOF)
|
|
||||||
Defines how and if line numbers should be shown
|
|
||||||
|
|
||||||
The possible values are :table, :inline, :list or nil. If this option is
|
|
||||||
nil, no line numbers are shown.
|
|
||||||
|
|
||||||
Default: :inline
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_line_number_start, Integer, 1, <<EOF)
|
|
||||||
The start value for the line numbers
|
|
||||||
|
|
||||||
Default: 1
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_tab_width, Integer, 8, <<EOF)
|
|
||||||
The tab width used in highlighted code
|
|
||||||
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_bold_every, Integer, 10, <<EOF)
|
|
||||||
Defines how often a line number should be made bold
|
|
||||||
|
|
||||||
Default: 10
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:coderay_css, Symbol, :style, <<EOF)
|
|
||||||
Defines how the highlighted code gets styled
|
|
||||||
|
|
||||||
Possible values are :class (CSS classes are applied to the code
|
|
||||||
elements, one must supply the needed CSS file) or :style (default CSS
|
|
||||||
styles are directly applied to the code elements).
|
|
||||||
|
|
||||||
Default: style
|
|
||||||
Used by: HTML converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:numeric_entities, Boolean, false, <<EOF)
|
|
||||||
Defines whether entities are output using names or numeric values
|
|
||||||
|
|
||||||
Default: false
|
|
||||||
Used by: HTML converter, kramdown converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
define(:toc_depth, Integer, 0, <<EOF)
|
|
||||||
Defines the maximum level of headers which will be used to generate the table of
|
|
||||||
contents. For instance, with a value of 2, toc entries will be generated for h1
|
|
||||||
and h2 headers but not for h3, h4, etc. A value of 0 uses all header levels.
|
|
||||||
|
|
||||||
Default: 0
|
|
||||||
Used by: HTML/Latex converter
|
|
||||||
EOF
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# == Parser Module
|
|
||||||
#
|
|
||||||
# This module contains all available parsers. Currently, there two parsers:
|
|
||||||
#
|
|
||||||
# * Kramdown for parsing documents in kramdown format
|
|
||||||
# * Html for parsing HTML documents
|
|
||||||
module Parser
|
|
||||||
|
|
||||||
autoload :Base, 'kramdown/parser/base'
|
|
||||||
autoload :Kramdown, 'kramdown/parser/kramdown'
|
|
||||||
autoload :Html, 'kramdown/parser/html'
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,94 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Parser
|
|
||||||
|
|
||||||
# == Base class for parsers
|
|
||||||
#
|
|
||||||
# This class serves as base class for parsers. It provides common methods that can/should be
|
|
||||||
# used by all parsers, especially by those using StringScanner for parsing.
|
|
||||||
#
|
|
||||||
class Base
|
|
||||||
|
|
||||||
# Initialize the parser with the given Kramdown document +doc+.
|
|
||||||
def initialize(doc)
|
|
||||||
@doc = doc
|
|
||||||
@text_type = :text
|
|
||||||
end
|
|
||||||
private_class_method(:new, :allocate)
|
|
||||||
|
|
||||||
# Parse the +source+ string into an element tree, using the information provided by the
|
|
||||||
# Kramdown document +doc+.
|
|
||||||
#
|
|
||||||
# Initializes a new instance of the calling class and then calls the #parse method that must
|
|
||||||
# be implemented by each subclass.
|
|
||||||
def self.parse(source, doc)
|
|
||||||
new(doc).parse(source)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Add the given warning +text+ to the warning array of the Kramdown document.
|
|
||||||
def warning(text)
|
|
||||||
@doc.warnings << text
|
|
||||||
#TODO: add position information
|
|
||||||
end
|
|
||||||
|
|
||||||
# Modify the string +source+ to be usable by the parser.
|
|
||||||
def adapt_source(source)
|
|
||||||
source.gsub(/\r\n?/, "\n").chomp + "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
# This helper method adds the given +text+ either to the last element in the +tree+ if it is a
|
|
||||||
# +type+ element or creates a new text element with the given +type+.
|
|
||||||
def add_text(text, tree = @tree, type = @text_type)
|
|
||||||
if tree.children.last && tree.children.last.type == type
|
|
||||||
tree.children.last.value << text
|
|
||||||
elsif !text.empty?
|
|
||||||
tree.children << Element.new(type, text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Extract the part of the StringScanner +srcscan+ backed string specified by the +range+. This
|
|
||||||
# method also works correctly under Ruby 1.9.
|
|
||||||
def extract_string(range, strscan)
|
|
||||||
result = nil
|
|
||||||
if RUBY_VERSION >= '1.9'
|
|
||||||
begin
|
|
||||||
enc = strscan.string.encoding
|
|
||||||
strscan.string.force_encoding('ASCII-8BIT')
|
|
||||||
result = strscan.string[range].force_encoding(enc)
|
|
||||||
ensure
|
|
||||||
strscan.string.force_encoding(enc)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result = strscan.string[range]
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,482 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'rexml/parsers/baseparser'
|
|
||||||
require 'strscan'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Parser
|
|
||||||
|
|
||||||
# Used for parsing a HTML document.
|
|
||||||
class Html < Base
|
|
||||||
|
|
||||||
# Contains all constants that are used when parsing.
|
|
||||||
module Constants
|
|
||||||
#:stopdoc:
|
|
||||||
# The following regexps are based on the ones used by REXML, with some slight modifications.
|
|
||||||
HTML_DOCTYPE_RE = /<!DOCTYPE.*?>/m
|
|
||||||
HTML_COMMENT_RE = /<!--(.*?)-->/m
|
|
||||||
HTML_INSTRUCTION_RE = /<\?(.*?)\?>/m
|
|
||||||
HTML_ATTRIBUTE_RE = /\s*(#{REXML::Parsers::BaseParser::UNAME_STR})\s*=\s*(["'])(.*?)\2/m
|
|
||||||
HTML_TAG_RE = /<((?>#{REXML::Parsers::BaseParser::UNAME_STR}))\s*((?>\s+#{REXML::Parsers::BaseParser::UNAME_STR}\s*=\s*(["']).*?\3)*)\s*(\/)?>/m
|
|
||||||
HTML_TAG_CLOSE_RE = /<\/(#{REXML::Parsers::BaseParser::NAME_STR})\s*>/m
|
|
||||||
HTML_ENTITY_RE = /&([\w:][\-\w\d\.:]*);|&#(\d+);|&\#x([0-9a-fA-F]+);/
|
|
||||||
|
|
||||||
|
|
||||||
HTML_PARSE_AS_BLOCK = %w{applet button blockquote colgroup dd div dl fieldset form iframe li
|
|
||||||
map noscript object ol table tbody thead tfoot tr td ul}
|
|
||||||
HTML_PARSE_AS_SPAN = %w{a abbr acronym address b bdo big cite caption del dfn dt em
|
|
||||||
h1 h2 h3 h4 h5 h6 i ins kbd label legend optgroup p q rb rbc
|
|
||||||
rp rt rtc ruby samp select small span strong sub sup th tt var}
|
|
||||||
HTML_PARSE_AS_RAW = %w{script math option textarea pre code}
|
|
||||||
|
|
||||||
HTML_PARSE_AS = Hash.new {|h,k| h[k] = :raw}
|
|
||||||
HTML_PARSE_AS_BLOCK.each {|i| HTML_PARSE_AS[i] = :block}
|
|
||||||
HTML_PARSE_AS_SPAN.each {|i| HTML_PARSE_AS[i] = :span}
|
|
||||||
HTML_PARSE_AS_RAW.each {|i| HTML_PARSE_AS[i] = :raw}
|
|
||||||
|
|
||||||
# Some HTML elements like script belong to both categories (i.e. are valid in block and
|
|
||||||
# span HTML) and don't appear therefore!
|
|
||||||
HTML_SPAN_ELEMENTS = %w{a abbr acronym b big bdo br button cite code del dfn em i img input
|
|
||||||
ins kbd label option q rb rbc rp rt rtc ruby samp select small span
|
|
||||||
strong sub sup textarea tt var}
|
|
||||||
HTML_BLOCK_ELEMENTS = %w{address article aside applet body button blockquote caption col colgroup dd div dl dt fieldset
|
|
||||||
figcaption footer form h1 h2 h3 h4 h5 h6 header hgroup hr html head iframe legend listing menu
|
|
||||||
li map nav ol optgroup p pre section summary table tbody td th thead tfoot tr ul}
|
|
||||||
HTML_ELEMENTS_WITHOUT_BODY = %w{area base br col command embed hr img input keygen link meta param source track wbr}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Contains the parsing methods. This module can be mixed into any parser to get HTML parsing
|
|
||||||
# functionality. The only thing that must be provided by the class are instance variable
|
|
||||||
# <tt>@stack</tt> for storing needed state and <tt>@src</tt> (instance of StringScanner) for
|
|
||||||
# the actual parsing.
|
|
||||||
module Parser
|
|
||||||
|
|
||||||
include Constants
|
|
||||||
|
|
||||||
# Process the HTML start tag that has already be scanned/checked. Does the common processing
|
|
||||||
# steps and then yields to the caller for further processing.
|
|
||||||
def handle_html_start_tag
|
|
||||||
name = @src[1]
|
|
||||||
closed = !@src[4].nil?
|
|
||||||
attrs = {}
|
|
||||||
@src[2].scan(HTML_ATTRIBUTE_RE).each {|attr,sep,val| attrs[attr] = val}
|
|
||||||
|
|
||||||
el = Element.new(:html_element, name, :attr => attrs, :category => :block)
|
|
||||||
@tree.children << el
|
|
||||||
|
|
||||||
if !closed && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
|
|
||||||
warning("The HTML tag '#{el.value}' cannot have any content - auto-closing it")
|
|
||||||
closed = true
|
|
||||||
end
|
|
||||||
if name == 'script'
|
|
||||||
handle_html_script_tag
|
|
||||||
yield(el, true)
|
|
||||||
else
|
|
||||||
yield(el, closed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_html_script_tag
|
|
||||||
curpos = @src.pos
|
|
||||||
if result = @src.scan_until(/(?=<\/script\s*>)/m)
|
|
||||||
add_text(extract_string(curpos...@src.pos, @src), @tree.children.last, :raw)
|
|
||||||
@src.scan(HTML_TAG_CLOSE_RE)
|
|
||||||
else
|
|
||||||
add_text(@src.scan(/.*/m), @tree.children.last, :raw)
|
|
||||||
warning("Found no end tag for 'script' - auto-closing it")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
HTML_RAW_START = /(?=<(#{REXML::Parsers::BaseParser::UNAME_STR}|\/|!--|\?))/
|
|
||||||
|
|
||||||
# Parse raw HTML from the current source position, storing the found elements in +el+.
|
|
||||||
# Parsing continues until one of the following criteria are fulfilled:
|
|
||||||
#
|
|
||||||
# - The end of the document is reached.
|
|
||||||
# - The matching end tag for the element +el+ is found (only used if +el+ is an HTML
|
|
||||||
# element).
|
|
||||||
#
|
|
||||||
# When an HTML start tag is found, processing is deferred to #handle_html_start_tag,
|
|
||||||
# providing the block given to this method.
|
|
||||||
def parse_raw_html(el, &block)
|
|
||||||
@stack.push(@tree)
|
|
||||||
@tree = el
|
|
||||||
|
|
||||||
done = false
|
|
||||||
while !@src.eos? && !done
|
|
||||||
if result = @src.scan_until(HTML_RAW_START)
|
|
||||||
add_text(result, @tree, :text)
|
|
||||||
if result = @src.scan(HTML_COMMENT_RE)
|
|
||||||
@tree.children << Element.new(:xml_comment, result, :category => :block, :parent_is_raw => true)
|
|
||||||
elsif result = @src.scan(HTML_INSTRUCTION_RE)
|
|
||||||
@tree.children << Element.new(:xml_pi, result, :category => :block, :parent_is_raw => true)
|
|
||||||
elsif @src.scan(HTML_TAG_RE)
|
|
||||||
handle_html_start_tag(&block)
|
|
||||||
elsif @src.scan(HTML_TAG_CLOSE_RE)
|
|
||||||
if @tree.value == @src[1]
|
|
||||||
done = true
|
|
||||||
else
|
|
||||||
warning("Found invalidly used HTML closing tag for '#{@src[1]}' - ignoring it")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
add_text(@src.scan(/./), @tree, :text)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result = @src.scan(/.*/m)
|
|
||||||
add_text(result, @tree, :text)
|
|
||||||
warning("Found no end tag for '#{@tree.value}' - auto-closing it") if @tree.type == :html_element
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree = @stack.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Converts HTML elements to native elements if possible.
|
|
||||||
class ElementConverter
|
|
||||||
|
|
||||||
include Constants
|
|
||||||
include ::Kramdown::Utils::Entities
|
|
||||||
|
|
||||||
REMOVE_TEXT_CHILDREN = %w{html head hgroup ol ul dl table colgroup tbody thead tfoot tr select optgroup}
|
|
||||||
WRAP_TEXT_CHILDREN = %w{body section nav article aside header footer address div li dd blockquote figure
|
|
||||||
figcaption fieldset form}
|
|
||||||
REMOVE_WHITESPACE_CHILDREN = %w{body section nav article aside header footer address
|
|
||||||
div li dd blockquote figure figcaption td th fieldset form}
|
|
||||||
STRIP_WHITESPACE = %w{address article aside blockquote body caption dd div dl dt fieldset figcaption form footer
|
|
||||||
header h1 h2 h3 h4 h5 h6 legend li nav p section td th}
|
|
||||||
SIMPLE_ELEMENTS = %w{em strong blockquote hr br a img p thead tbody tfoot tr td th ul ol dl li dl dt dd}
|
|
||||||
|
|
||||||
def initialize(doc)
|
|
||||||
@doc = doc
|
|
||||||
end
|
|
||||||
|
|
||||||
# Convert the element +el+ and its children.
|
|
||||||
def process(el, do_conversion = true, preserve_text = false, parent = nil)
|
|
||||||
case el.type
|
|
||||||
when :xml_comment, :xml_pi, :html_doctype
|
|
||||||
ptype = if parent.nil?
|
|
||||||
'div'
|
|
||||||
else
|
|
||||||
case parent.type
|
|
||||||
when :html_element then parent.value
|
|
||||||
when :code_span then 'code'
|
|
||||||
when :code_block then 'pre'
|
|
||||||
when :header then 'h1'
|
|
||||||
else parent.type.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
el.options = {:category => HTML_PARSE_AS_SPAN.include?(ptype) ? :span : :block}
|
|
||||||
return
|
|
||||||
when :html_element
|
|
||||||
else return
|
|
||||||
end
|
|
||||||
|
|
||||||
type = el.value
|
|
||||||
remove_text_children(el) if REMOVE_TEXT_CHILDREN.include?(type)
|
|
||||||
|
|
||||||
mname = "convert_#{el.value}"
|
|
||||||
if do_conversion && self.class.method_defined?(mname)
|
|
||||||
send(mname, el)
|
|
||||||
elsif do_conversion && SIMPLE_ELEMENTS.include?(type)
|
|
||||||
set_basics(el, type.intern, HTML_SPAN_ELEMENTS.include?(type) ? :span : :block)
|
|
||||||
process_children(el, do_conversion, preserve_text)
|
|
||||||
else
|
|
||||||
process_html_element(el, do_conversion, preserve_text)
|
|
||||||
end
|
|
||||||
|
|
||||||
strip_whitespace(el) if STRIP_WHITESPACE.include?(type)
|
|
||||||
remove_whitespace_children(el) if REMOVE_WHITESPACE_CHILDREN.include?(type)
|
|
||||||
wrap_text_children(el) if WRAP_TEXT_CHILDREN.include?(type)
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_children(el, do_conversion = true, preserve_text = false)
|
|
||||||
el.children.map! do |c|
|
|
||||||
if c.type == :text
|
|
||||||
process_text(c.value, preserve_text)
|
|
||||||
else
|
|
||||||
process(c, do_conversion, preserve_text, el)
|
|
||||||
c
|
|
||||||
end
|
|
||||||
end.flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Process the HTML text +raw+: compress whitespace (if +preserve+ is +false+) and convert
|
|
||||||
# entities in entity elements.
|
|
||||||
def process_text(raw, preserve = false)
|
|
||||||
raw.gsub!(/\s+/, ' ') unless preserve
|
|
||||||
src = StringScanner.new(raw)
|
|
||||||
result = []
|
|
||||||
while !src.eos?
|
|
||||||
if tmp = src.scan_until(/(?=#{HTML_ENTITY_RE})/)
|
|
||||||
result << Element.new(:text, tmp)
|
|
||||||
src.scan(HTML_ENTITY_RE)
|
|
||||||
val = src[1] || (src[2] && src[2].to_i) || src[3].hex
|
|
||||||
result << if %w{lsquo rsquo ldquo rdquo}.include?(val)
|
|
||||||
Element.new(:smart_quote, val.intern)
|
|
||||||
elsif %w{mdash ndash hellip laquo raquo}.include?(val)
|
|
||||||
Element.new(:typographic_sym, val.intern)
|
|
||||||
else
|
|
||||||
Element.new(:entity, entity(val))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result << Element.new(:text, src.scan(/.*/m))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_html_element(el, do_conversion = true, preserve_text = false)
|
|
||||||
el.options = {:category => HTML_SPAN_ELEMENTS.include?(el.value) ? :span : :block,
|
|
||||||
:parse_type => HTML_PARSE_AS[el.value],
|
|
||||||
:attr => el.options[:attr]
|
|
||||||
}
|
|
||||||
process_children(el, do_conversion, preserve_text)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_text_children(el)
|
|
||||||
el.children.delete_if {|c| c.type == :text}
|
|
||||||
end
|
|
||||||
|
|
||||||
SPAN_ELEMENTS = [:em, :strong, :br, :a, :img, :codespan, :entity, :smart_quote, :typographic_sym, :math]
|
|
||||||
|
|
||||||
def wrap_text_children(el)
|
|
||||||
tmp = []
|
|
||||||
last_is_p = false
|
|
||||||
el.children.each do |c|
|
|
||||||
if c.options[:category] != :block || c.type == :text
|
|
||||||
if !last_is_p
|
|
||||||
tmp << Element.new(:p, nil, :transparent => true)
|
|
||||||
last_is_p = true
|
|
||||||
end
|
|
||||||
tmp.last.children << c
|
|
||||||
tmp
|
|
||||||
else
|
|
||||||
tmp << c
|
|
||||||
last_is_p = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
el.children = tmp
|
|
||||||
end
|
|
||||||
|
|
||||||
def strip_whitespace(el)
|
|
||||||
return if el.children.empty?
|
|
||||||
if el.children.first.type == :text
|
|
||||||
el.children.first.value.lstrip!
|
|
||||||
end
|
|
||||||
if el.children.last.type == :text
|
|
||||||
el.children.last.value.rstrip!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_whitespace_children(el)
|
|
||||||
i = -1
|
|
||||||
el.children.delete_if do |c|
|
|
||||||
i += 1
|
|
||||||
c.type == :text && c.value.strip.empty? &&
|
|
||||||
(i == 0 || i == el.children.length - 1 || (el.children[i-1].options[:category] == :block &&
|
|
||||||
el.children[i+1].options[:category] == :block))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_basics(el, type, category, opts = {})
|
|
||||||
el.type = type
|
|
||||||
el.options = {:category => category, :attr => el.options[:attr]}.merge(opts)
|
|
||||||
el.value = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def extract_text(el, raw)
|
|
||||||
raw << el.value.to_s if el.type == :text
|
|
||||||
el.children.each {|c| extract_text(c, raw)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_h1(el)
|
|
||||||
set_basics(el, :header, :block, :level => el.value[1..1].to_i)
|
|
||||||
extract_text(el, el.options[:raw_text] = '')
|
|
||||||
process_children(el)
|
|
||||||
end
|
|
||||||
%w{h2 h3 h4 h5 h6}.each do |i|
|
|
||||||
alias_method("convert_#{i}".to_sym, :convert_h1)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_code(el)
|
|
||||||
raw = ''
|
|
||||||
extract_text(el, raw)
|
|
||||||
result = process_text(raw, true)
|
|
||||||
begin
|
|
||||||
str = result.inject('') do |mem, c|
|
|
||||||
if c.type == :text
|
|
||||||
mem << c.value
|
|
||||||
elsif c.type == :entity
|
|
||||||
if RUBY_VERSION >= '1.9'
|
|
||||||
mem << c.value.char.encode(@doc.parse_infos[:encoding])
|
|
||||||
elsif [60, 62, 34, 38].include?(c.value.code_point)
|
|
||||||
mem << c.value.code_point.chr
|
|
||||||
end
|
|
||||||
elsif c.type == :smart_quote || c.type == :typographic_sym
|
|
||||||
mem << entity(c.value.to_s).char.encode(@doc.parse_infos[:encoding])
|
|
||||||
else
|
|
||||||
raise "Bug - please report"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
result.clear
|
|
||||||
result << Element.new(:text, str)
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
if result.length > 1 || result.first.type != :text
|
|
||||||
process_html_element(el, false, true)
|
|
||||||
else
|
|
||||||
if el.value == 'code'
|
|
||||||
set_basics(el, :codespan, :span)
|
|
||||||
else
|
|
||||||
set_basics(el, :codeblock, :block)
|
|
||||||
end
|
|
||||||
el.value = result.first.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_pre :convert_code
|
|
||||||
|
|
||||||
def convert_table(el)
|
|
||||||
if !is_simple_table?(el)
|
|
||||||
process_html_element(el, false)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
process_children(el)
|
|
||||||
set_basics(el, :table, :block)
|
|
||||||
el.options[:alignment] = []
|
|
||||||
calc_alignment = lambda do |c|
|
|
||||||
if c.type == :tr && el.options[:alignment].empty?
|
|
||||||
el.options[:alignment] = [:default] * c.children.length
|
|
||||||
break
|
|
||||||
else
|
|
||||||
c.children.each {|cc| calc_alignment.call(cc)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
calc_alignment.call(el)
|
|
||||||
if el.children.first.type == :tr
|
|
||||||
tbody = Element.new(:tbody, nil, :category => :block)
|
|
||||||
tbody.children = el.children
|
|
||||||
el.children = [tbody]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_simple_table?(el)
|
|
||||||
only_phrasing_content = lambda do |c|
|
|
||||||
c.children.all? do |cc|
|
|
||||||
(cc.type == :text || !HTML_BLOCK_ELEMENTS.include?(cc.value)) && only_phrasing_content.call(cc)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
check_cells = Proc.new do |c|
|
|
||||||
if c.value == 'th' || c.value == 'td'
|
|
||||||
return false if !only_phrasing_content.call(c)
|
|
||||||
else
|
|
||||||
c.children.each {|cc| check_cells.call(cc)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
check_cells.call(el)
|
|
||||||
|
|
||||||
check_rows = lambda do |t, type|
|
|
||||||
t.children.all? {|r| (r.value == 'tr' || r.type == :text) && r.children.all? {|c| c.value == type || c.type == :text}}
|
|
||||||
end
|
|
||||||
check_rows.call(el, 'td') ||
|
|
||||||
(el.children.all? do |t|
|
|
||||||
t.type == :text || (t.value == 'thead' && check_rows.call(t, 'th')) ||
|
|
||||||
((t.value == 'tfoot' || t.value == 'tbody') && check_rows.call(t, 'td'))
|
|
||||||
end && el.children.any? {|t| t.value == 'tbody'})
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_div(el)
|
|
||||||
if !is_math_tag?(el)
|
|
||||||
process_html_element(el)
|
|
||||||
else
|
|
||||||
handle_math_tag(el)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :convert_span :convert_div
|
|
||||||
|
|
||||||
def is_math_tag?(el)
|
|
||||||
el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bmath\b/ &&
|
|
||||||
el.children.size == 1 && el.children.first.type == :text
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_math_tag(el)
|
|
||||||
set_basics(el, :math, (el.value == 'div' ? :block : :span))
|
|
||||||
el.value = el.children.shift.value
|
|
||||||
if el.options[:attr]['class'] =~ /^\s*math\s*$/
|
|
||||||
el.options[:attr].delete('class')
|
|
||||||
else
|
|
||||||
el.options[:attr]['class'].sub!(/\s?math/, '')
|
|
||||||
end
|
|
||||||
el.value.gsub!(/&(amp|quot|gt|lt);/) do |m|
|
|
||||||
case m
|
|
||||||
when '&' then '&'
|
|
||||||
when '"' then '"'
|
|
||||||
when '>' then '>'
|
|
||||||
when '<' then '<'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
include Parser
|
|
||||||
|
|
||||||
# Parse +source+ as HTML document and return the created +tree+.
|
|
||||||
def parse(source)
|
|
||||||
@stack = []
|
|
||||||
@tree = Element.new(:root)
|
|
||||||
@src = StringScanner.new(adapt_source(source))
|
|
||||||
|
|
||||||
while true
|
|
||||||
if result = @src.scan(/\s*#{HTML_INSTRUCTION_RE}/)
|
|
||||||
@tree.children << Element.new(:xml_pi, result.strip, :category => :block)
|
|
||||||
elsif result = @src.scan(/\s*#{HTML_DOCTYPE_RE}/)
|
|
||||||
@tree.children << Element.new(:html_doctype, result.strip, :category => :block)
|
|
||||||
elsif result = @src.scan(/\s*#{HTML_COMMENT_RE}/)
|
|
||||||
@tree.children << Element.new(:xml_comment, result.strip, :category => :block)
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tag_handler = lambda do |c, closed|
|
|
||||||
parse_raw_html(c, &tag_handler) if !closed
|
|
||||||
end
|
|
||||||
parse_raw_html(@tree, &tag_handler)
|
|
||||||
|
|
||||||
ec = ElementConverter.new(@doc)
|
|
||||||
@tree.children.each {|c| ec.process(c)}
|
|
||||||
ec.remove_whitespace_children(@tree)
|
|
||||||
@tree
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'strscan'
|
|
||||||
require 'stringio'
|
|
||||||
|
|
||||||
#TODO: use [[:alpha:]] in all regexp to allow parsing of international values in 1.9.1
|
|
||||||
#NOTE: use @src.pre_match only before other check/match?/... operations, otherwise the content is changed
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Parser
|
|
||||||
|
|
||||||
# Used for parsing a document in kramdown format.
|
|
||||||
#
|
|
||||||
# If you want to extend the functionality of the parser, you need to the following:
|
|
||||||
#
|
|
||||||
# * Create a new subclass
|
|
||||||
# * add the needed parser methods
|
|
||||||
# * modify the @block_parsers and @span_parsers variables and add the names of your parser
|
|
||||||
# methods
|
|
||||||
#
|
|
||||||
# Here is a small example for an extended parser class that parses ERB style tags as raw text if
|
|
||||||
# they are used as span level elements (an equivalent block level parser should probably also be
|
|
||||||
# made to handle the block case):
|
|
||||||
#
|
|
||||||
# require 'kramdown/parser/kramdown'
|
|
||||||
#
|
|
||||||
# class Kramdown::Parser::ERBKramdown < Kramdown::Parser::Kramdown
|
|
||||||
#
|
|
||||||
# def initialize(doc)
|
|
||||||
# super(doc)
|
|
||||||
# @span_parsers.unshift(:erb_tags)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# ERB_TAGS_START = /<%.*?%>/
|
|
||||||
#
|
|
||||||
# def parse_erb_tags
|
|
||||||
# @src.pos += @src.matched_size
|
|
||||||
# @tree.children << Element.new(:raw, @src.matched)
|
|
||||||
# end
|
|
||||||
# define_parser(:erb_tags, ERB_TAGS_START, '<%')
|
|
||||||
#
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# The new parser can be used like this:
|
|
||||||
#
|
|
||||||
# require 'kramdown/document'
|
|
||||||
# # require the file with the above parser class
|
|
||||||
#
|
|
||||||
# Kramdown::Document.new(input_text, :input => 'ERBKramdown').to_html
|
|
||||||
#
|
|
||||||
class Kramdown < Base
|
|
||||||
|
|
||||||
include ::Kramdown
|
|
||||||
|
|
||||||
attr_reader :tree
|
|
||||||
attr_reader :doc
|
|
||||||
attr_reader :options
|
|
||||||
|
|
||||||
# Create a new Kramdown parser object for the Kramdown::Document +doc+.
|
|
||||||
def initialize(doc)
|
|
||||||
super(doc)
|
|
||||||
|
|
||||||
@src = nil
|
|
||||||
@tree = nil
|
|
||||||
@stack = []
|
|
||||||
@text_type = :raw_text
|
|
||||||
@block_ial = nil
|
|
||||||
|
|
||||||
@doc.parse_infos[:ald] = {}
|
|
||||||
@doc.parse_infos[:link_defs] = {}
|
|
||||||
@doc.parse_infos[:abbrev_defs] = {}
|
|
||||||
@doc.parse_infos[:footnotes] = {}
|
|
||||||
|
|
||||||
@block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :table, :atx_header,
|
|
||||||
:setext_header, :horizontal_rule, :list, :definition_list, :link_definition, :block_html,
|
|
||||||
:footnote_definition, :abbrev_definition, :ald, :block_math,
|
|
||||||
:block_extension, :block_ial, :eob_marker, :paragraph]
|
|
||||||
@span_parsers = [:emphasis, :codespan, :autolink, :span_html, :footnote_marker, :link, :smart_quotes, :inline_math,
|
|
||||||
:span_extension, :span_ial, :html_entity, :typographic_syms, :line_break, :escaped_chars]
|
|
||||||
|
|
||||||
end
|
|
||||||
private_class_method(:new, :allocate)
|
|
||||||
|
|
||||||
|
|
||||||
# The source string provided on initialization is parsed and the created +tree+ is returned.
|
|
||||||
def parse(source)
|
|
||||||
configure_parser
|
|
||||||
tree = Element.new(:root)
|
|
||||||
parse_blocks(tree, adapt_source(source))
|
|
||||||
update_tree(tree)
|
|
||||||
replace_abbreviations(tree)
|
|
||||||
@doc.parse_infos[:footnotes].each do |name, data|
|
|
||||||
update_tree(data[:content])
|
|
||||||
end
|
|
||||||
tree
|
|
||||||
end
|
|
||||||
|
|
||||||
#######
|
|
||||||
protected
|
|
||||||
#######
|
|
||||||
|
|
||||||
# Adapt the object to allow parsing like specified in the options.
|
|
||||||
def configure_parser
|
|
||||||
@parsers = {}
|
|
||||||
(@block_parsers + @span_parsers).each do |name|
|
|
||||||
if self.class.has_parser?(name)
|
|
||||||
@parsers[name] = self.class.parser(name)
|
|
||||||
else
|
|
||||||
raise Kramdown::Error, "Unknown parser: #{name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@span_start, @span_start_re = span_parser_regexps
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create the needed span parser regexps.
|
|
||||||
def span_parser_regexps(parsers = @span_parsers)
|
|
||||||
span_start = /#{parsers.map {|name| @parsers[name].span_start}.join('|')}/
|
|
||||||
[span_start, /(?=#{span_start})/]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse all block level elements in +text+ into the element +el+.
|
|
||||||
def parse_blocks(el, text = nil)
|
|
||||||
@stack.push([@tree, @src])
|
|
||||||
@tree, @src = el, (text.nil? ? @src : StringScanner.new(text))
|
|
||||||
|
|
||||||
status = catch(:stop_block_parsing) do
|
|
||||||
while !@src.eos?
|
|
||||||
block_ial_set = @block_ial
|
|
||||||
@block_parsers.any? do |name|
|
|
||||||
if @src.check(@parsers[name].start_re)
|
|
||||||
send(@parsers[name].method)
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end || begin
|
|
||||||
warning('Warning: this should not occur - no block parser handled the line')
|
|
||||||
add_text(@src.scan(/.*\n/))
|
|
||||||
end
|
|
||||||
@block_ial = nil if block_ial_set
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree, @src = *@stack.pop
|
|
||||||
status
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the tree by parsing all <tt>:raw_text</tt> elements with the span level parser
|
|
||||||
# (resets +@tree+, +@src+ and the +@stack+) and by updating the attributes from the IALs.
|
|
||||||
def update_tree(element)
|
|
||||||
element.children.map! do |child|
|
|
||||||
if child.type == :raw_text
|
|
||||||
@stack, @tree, @text_type = [], nil, :text
|
|
||||||
@src = StringScanner.new(child.value)
|
|
||||||
parse_spans(child)
|
|
||||||
child.children
|
|
||||||
elsif child.type == :eob
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
update_tree(child)
|
|
||||||
update_attr_with_ial(child.options[:attr] ||= {}, child.options[:ial]) if child.options[:ial]
|
|
||||||
child
|
|
||||||
end
|
|
||||||
end.flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse all span level elements in the source string.
|
|
||||||
def parse_spans(el, stop_re = nil, parsers = nil, text_type = @text_type)
|
|
||||||
@stack.push([@tree, @text_type]) unless @tree.nil?
|
|
||||||
@tree, @text_type = el, text_type
|
|
||||||
|
|
||||||
span_start = @span_start
|
|
||||||
span_start_re = @span_start_re
|
|
||||||
span_start, span_start_re = span_parser_regexps(parsers) if parsers
|
|
||||||
parsers = parsers || @span_parsers
|
|
||||||
|
|
||||||
used_re = (stop_re.nil? ? span_start_re : /(?=#{Regexp.union(stop_re, span_start)})/)
|
|
||||||
stop_re_found = false
|
|
||||||
while !@src.eos? && !stop_re_found
|
|
||||||
if result = @src.scan_until(used_re)
|
|
||||||
add_text(result)
|
|
||||||
if stop_re && (stop_re_matched = @src.check(stop_re))
|
|
||||||
stop_re_found = (block_given? ? yield : true)
|
|
||||||
end
|
|
||||||
processed = parsers.any? do |name|
|
|
||||||
if @src.check(@parsers[name].start_re)
|
|
||||||
send(@parsers[name].method)
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end unless stop_re_found
|
|
||||||
add_text(@src.scan(/./)) if !processed && !stop_re_found
|
|
||||||
else
|
|
||||||
add_text(@src.scan(/.*/m)) unless stop_re
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree, @text_type = @stack.pop
|
|
||||||
|
|
||||||
stop_re_found
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the attributes with the information from the inline attribute list and all referenced ALDs.
|
|
||||||
def update_attr_with_ial(attr, ial)
|
|
||||||
ial[:refs].each do |ref|
|
|
||||||
update_attr_with_ial(attr, ref) if ref = @doc.parse_infos[:ald][ref]
|
|
||||||
end if ial[:refs]
|
|
||||||
attr['class'] = ((attr['class'] || '') + " #{ial['class']}").lstrip if ial['class']
|
|
||||||
ial.each {|k,v| attr[k] = v if k.kind_of?(String) && k != 'class' }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a new block level element, taking care of applying a preceding block IAL if it exists.
|
|
||||||
def new_block_el(*args)
|
|
||||||
el = Element.new(*args)
|
|
||||||
el.options[:category] ||= :block
|
|
||||||
el.options[:ial] = @block_ial if @block_ial && el.type != :blank && el.type != :eob
|
|
||||||
el
|
|
||||||
end
|
|
||||||
|
|
||||||
@@parsers = {}
|
|
||||||
|
|
||||||
# Holds all the needed data for one block/span level parser.
|
|
||||||
Data = Struct.new(:name, :start_re, :span_start, :method)
|
|
||||||
|
|
||||||
# Add a parser method
|
|
||||||
#
|
|
||||||
# * with the given +name+,
|
|
||||||
# * using +start_re+ as start regexp
|
|
||||||
# * and, for span parsers, +span_start+ as a String that can be used in a regexp and
|
|
||||||
# which identifies the starting character(s)
|
|
||||||
#
|
|
||||||
# to the registry. The method name is automatically derived from the +name+ or can explicitly
|
|
||||||
# be set by using the +meth_name+ parameter.
|
|
||||||
def self.define_parser(name, start_re, span_start = nil, meth_name = "parse_#{name}")
|
|
||||||
raise "A parser with the name #{name} already exists!" if @@parsers.has_key?(name)
|
|
||||||
@@parsers[name] = Data.new(name, start_re, span_start, meth_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the Data structure for the parser +name+.
|
|
||||||
def self.parser(name = nil)
|
|
||||||
@@parsers[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return +true+ if there is a parser called +name+.
|
|
||||||
def self.has_parser?(name)
|
|
||||||
@@parsers.has_key?(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
INDENT = /^(?:\t| {4})/
|
|
||||||
OPT_SPACE = / {0,3}/
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/blank_line'
|
|
||||||
require 'kramdown/parser/kramdown/eob'
|
|
||||||
require 'kramdown/parser/kramdown/paragraph'
|
|
||||||
require 'kramdown/parser/kramdown/header'
|
|
||||||
require 'kramdown/parser/kramdown/blockquote'
|
|
||||||
require 'kramdown/parser/kramdown/table'
|
|
||||||
require 'kramdown/parser/kramdown/codeblock'
|
|
||||||
require 'kramdown/parser/kramdown/horizontal_rule'
|
|
||||||
require 'kramdown/parser/kramdown/list'
|
|
||||||
require 'kramdown/parser/kramdown/link'
|
|
||||||
require 'kramdown/parser/kramdown/attribute_list'
|
|
||||||
require 'kramdown/parser/kramdown/extension'
|
|
||||||
require 'kramdown/parser/kramdown/footnote'
|
|
||||||
require 'kramdown/parser/kramdown/html'
|
|
||||||
require 'kramdown/parser/kramdown/escaped_chars'
|
|
||||||
require 'kramdown/parser/kramdown/html_entity'
|
|
||||||
require 'kramdown/parser/kramdown/line_break'
|
|
||||||
require 'kramdown/parser/kramdown/typographic_symbol'
|
|
||||||
require 'kramdown/parser/kramdown/autolink'
|
|
||||||
require 'kramdown/parser/kramdown/codespan'
|
|
||||||
require 'kramdown/parser/kramdown/emphasis'
|
|
||||||
require 'kramdown/parser/kramdown/smart_quotes'
|
|
||||||
require 'kramdown/parser/kramdown/math'
|
|
||||||
require 'kramdown/parser/kramdown/abbreviation'
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,65 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
ABBREV_DEFINITION_START = /^#{OPT_SPACE}\*\[(.+?)\]:(.*?)\n/
|
|
||||||
|
|
||||||
# Parse the link definition at the current location.
|
|
||||||
def parse_abbrev_definition
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
abbrev_id, abbrev_text = @src[1], @src[2].strip
|
|
||||||
warning("Duplicate abbreviation ID '#{abbrev_id}' - overwriting") if @doc.parse_infos[:abbrev_defs][abbrev_id]
|
|
||||||
@doc.parse_infos[:abbrev_defs][abbrev_id] = abbrev_text
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:abbrev_definition, ABBREV_DEFINITION_START)
|
|
||||||
|
|
||||||
# Replace the abbreviation text with elements.
|
|
||||||
def replace_abbreviations(el, regexps = nil)
|
|
||||||
return if @doc.parse_infos[:abbrev_defs].empty?
|
|
||||||
if !regexps
|
|
||||||
regexps = [Regexp.union(*@doc.parse_infos[:abbrev_defs].keys.map {|k| /#{Regexp.escape(k)}/})]
|
|
||||||
regexps << /(?=(?:\W|^)#{regexps.first}(?!\w))/ # regexp should only match on word boundaries
|
|
||||||
end
|
|
||||||
el.children.map! do |child|
|
|
||||||
if child.type == :text
|
|
||||||
result = []
|
|
||||||
strscan = StringScanner.new(child.value)
|
|
||||||
while temp = strscan.scan_until(regexps.last)
|
|
||||||
temp += strscan.scan(/\W|^/)
|
|
||||||
abbr = strscan.scan(regexps.first)
|
|
||||||
result += [Element.new(:text, temp), Element.new(:abbreviation, abbr)]
|
|
||||||
end
|
|
||||||
result + [Element.new(:text, extract_string(strscan.pos..-1, strscan))]
|
|
||||||
else
|
|
||||||
replace_abbreviations(child, regexps)
|
|
||||||
child
|
|
||||||
end
|
|
||||||
end.flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,105 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
# Parse the string +str+ and extract all attributes and add all found attributes to the hash
|
|
||||||
# +opts+.
|
|
||||||
def parse_attribute_list(str, opts)
|
|
||||||
str.scan(ALD_TYPE_ANY).each do |key, sep, val, id_attr, class_attr, ref|
|
|
||||||
if ref
|
|
||||||
(opts[:refs] ||= []) << ref
|
|
||||||
elsif class_attr
|
|
||||||
opts['class'] = ((opts['class'] || '') + " #{class_attr}").lstrip
|
|
||||||
elsif id_attr
|
|
||||||
opts['id'] = id_attr
|
|
||||||
else
|
|
||||||
opts[key] = val.gsub(/\\(\}|#{sep})/, "\\1")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the +ial+ with the information from the inline attribute list +opts+.
|
|
||||||
def update_ial_with_ial(ial, opts)
|
|
||||||
(ial[:refs] ||= []) << opts[:refs]
|
|
||||||
ial['class'] = ((ial['class'] || '') + " #{opts['class']}").lstrip if opts['class']
|
|
||||||
opts.each {|k,v| ial[k] = v if k != :refs && k != 'class' }
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
ALD_ID_CHARS = /[\w\d-]/
|
|
||||||
ALD_ANY_CHARS = /\\\}|[^\}]/
|
|
||||||
ALD_ID_NAME = /(?:\w|\d)#{ALD_ID_CHARS}*/
|
|
||||||
ALD_TYPE_KEY_VALUE_PAIR = /(#{ALD_ID_NAME})=("|')((?:\\\}|\\\2|[^\}\2])*?)\2/
|
|
||||||
ALD_TYPE_CLASS_NAME = /\.(#{ALD_ID_NAME})/
|
|
||||||
ALD_TYPE_ID_NAME = /#(#{ALD_ID_NAME})/
|
|
||||||
ALD_TYPE_REF = /(#{ALD_ID_NAME})/
|
|
||||||
ALD_TYPE_ANY = /(?:\A|\s)(?:#{ALD_TYPE_KEY_VALUE_PAIR}|#{ALD_TYPE_ID_NAME}|#{ALD_TYPE_CLASS_NAME}|#{ALD_TYPE_REF})(?=\s|\Z)/
|
|
||||||
ALD_START = /^#{OPT_SPACE}\{:(#{ALD_ID_NAME}):(#{ALD_ANY_CHARS}+)\}\s*?\n/
|
|
||||||
|
|
||||||
# Parse the attribute list definition at the current location.
|
|
||||||
def parse_ald
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
parse_attribute_list(@src[2], @doc.parse_infos[:ald][@src[1]] ||= {})
|
|
||||||
@tree.children << Element.new(:eob)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:ald, ALD_START)
|
|
||||||
|
|
||||||
|
|
||||||
IAL_BLOCK_START = /^#{OPT_SPACE}\{:(?!:)(#{ALD_ANY_CHARS}+)\}\s*?\n/
|
|
||||||
|
|
||||||
# Parse the inline attribute list at the current location.
|
|
||||||
def parse_block_ial
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
if @tree.children.last && @tree.children.last.type != :blank && @tree.children.last.type != :eob
|
|
||||||
parse_attribute_list(@src[1], @tree.children.last.options[:ial] ||= {})
|
|
||||||
else
|
|
||||||
parse_attribute_list(@src[1], @block_ial = {})
|
|
||||||
end
|
|
||||||
@tree.children << Element.new(:eob) unless @src.check(IAL_BLOCK_START)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:block_ial, IAL_BLOCK_START)
|
|
||||||
|
|
||||||
|
|
||||||
IAL_SPAN_START = /\{:(#{ALD_ANY_CHARS}+)\}/
|
|
||||||
|
|
||||||
# Parse the inline attribute list at the current location.
|
|
||||||
def parse_span_ial
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
if @tree.children.last && @tree.children.last.type != :text
|
|
||||||
attr = {}
|
|
||||||
parse_attribute_list(@src[1], attr)
|
|
||||||
update_ial_with_ial(@tree.children.last.options[:ial] ||= {}, attr)
|
|
||||||
update_attr_with_ial(@tree.children.last.options[:attr] ||= {}, attr)
|
|
||||||
else
|
|
||||||
warning("Ignoring span IAL because preceding element is just text")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:span_ial, IAL_SPAN_START, '\{:')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,47 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
if RUBY_VERSION == '1.8.5'
|
|
||||||
ACHARS = '\x80-\xFF'
|
|
||||||
else
|
|
||||||
ACHARS = ''
|
|
||||||
end
|
|
||||||
AUTOLINK_START = /<((mailto|https?|ftps?):.+?|[-.\w#{ACHARS}]+@[-\w#{ACHARS}]+(\.[-\w#{ACHARS}]+)*\.[a-z]+)>/u
|
|
||||||
|
|
||||||
# Parse the autolink at the current location.
|
|
||||||
def parse_autolink
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
href = @src[1]
|
|
||||||
href= "mailto:#{href}" if @src[2].nil?
|
|
||||||
el = Element.new(:a, nil, {:attr => {'href' => href}})
|
|
||||||
add_text(@src[1].sub(/^mailto:/, ''), el)
|
|
||||||
@tree.children << el
|
|
||||||
end
|
|
||||||
define_parser(:autolink, AUTOLINK_START, '<')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,43 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
BLANK_LINE = /(?:^\s*\n)+/
|
|
||||||
|
|
||||||
# Parse the blank line at the current postition.
|
|
||||||
def parse_blank_line
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
if @tree.children.last && @tree.children.last.type == :blank
|
|
||||||
@tree.children.last.value += @src.matched
|
|
||||||
else
|
|
||||||
@tree.children << new_block_el(:blank, @src.matched)
|
|
||||||
end
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:blank_line, BLANK_LINE)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,42 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
BLOCKQUOTE_START = /^#{OPT_SPACE}> ?/
|
|
||||||
BLOCKQUOTE_MATCH = /(^#{OPT_SPACE}>.*?\n)+/
|
|
||||||
|
|
||||||
# Parse the blockquote at the current location.
|
|
||||||
def parse_blockquote
|
|
||||||
result = @src.scan(BLOCKQUOTE_MATCH).gsub(BLOCKQUOTE_START, '')
|
|
||||||
el = new_block_el(:blockquote)
|
|
||||||
@tree.children << el
|
|
||||||
parse_blocks(el, result)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:blockquote, BLOCKQUOTE_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,58 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/blank_line'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
CODEBLOCK_START = INDENT
|
|
||||||
CODEBLOCK_LINE = /(?:#{INDENT}.*?\S.*?\n)+/
|
|
||||||
CODEBLOCK_MATCH = /(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*/
|
|
||||||
|
|
||||||
# Parse the indented codeblock at the current location.
|
|
||||||
def parse_codeblock
|
|
||||||
@tree.children << new_block_el(:codeblock, @src.scan(CODEBLOCK_MATCH).gsub!(INDENT, ''))
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:codeblock, CODEBLOCK_START)
|
|
||||||
|
|
||||||
|
|
||||||
FENCED_CODEBLOCK_START = /^~{3,}/
|
|
||||||
FENCED_CODEBLOCK_MATCH = /^(~{3,})\s*?\n(.*?)^\1~*\s*?\n/m
|
|
||||||
|
|
||||||
# Parse the fenced codeblock at the current location.
|
|
||||||
def parse_codeblock_fenced
|
|
||||||
if @src.check(FENCED_CODEBLOCK_MATCH)
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << new_block_el(:codeblock, @src[2])
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:codeblock_fenced, FENCED_CODEBLOCK_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,57 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
CODESPAN_DELIMITER = /`+/
|
|
||||||
|
|
||||||
# Parse the codespan at the current scanner location.
|
|
||||||
def parse_codespan
|
|
||||||
result = @src.scan(CODESPAN_DELIMITER)
|
|
||||||
simple = (result.length == 1)
|
|
||||||
reset_pos = @src.pos
|
|
||||||
|
|
||||||
if simple && @src.pre_match =~ /\s\Z/ && @src.match?(/\s/)
|
|
||||||
add_text(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
text = @src.scan_until(/#{result}/)
|
|
||||||
if text
|
|
||||||
text.sub!(/#{result}\Z/, '')
|
|
||||||
if !simple
|
|
||||||
text = text[1..-1] if text[0..0] == ' '
|
|
||||||
text = text[0..-2] if text[-1..-1] == ' '
|
|
||||||
end
|
|
||||||
@tree.children << Element.new(:codespan, text)
|
|
||||||
else
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:codespan, CODESPAN_DELIMITER, '`')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,70 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
EMPHASIS_START = /(?:\*\*?|__?)/
|
|
||||||
|
|
||||||
# Parse the emphasis at the current location.
|
|
||||||
def parse_emphasis
|
|
||||||
result = @src.scan(EMPHASIS_START)
|
|
||||||
element = (result.length == 2 ? :strong : :em)
|
|
||||||
type = (result =~ /_/ ? '_' : '*')
|
|
||||||
reset_pos = @src.pos
|
|
||||||
|
|
||||||
if (type == '_' && @src.pre_match =~ /[[:alpha:]]\z/ && @src.check(/[[:alpha:]]/)) || @src.check(/\s/) ||
|
|
||||||
@tree.type == element || @stack.any? {|el, _| el.type == element}
|
|
||||||
add_text(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
sub_parse = lambda do |delim, elem|
|
|
||||||
el = Element.new(elem)
|
|
||||||
stop_re = /#{Regexp.escape(delim)}/
|
|
||||||
found = parse_spans(el, stop_re) do
|
|
||||||
(@src.pre_match[-1, 1] !~ /\s/) &&
|
|
||||||
(elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) &&
|
|
||||||
(type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alpha:]]/)) && el.children.size > 0
|
|
||||||
end
|
|
||||||
[found, el, stop_re]
|
|
||||||
end
|
|
||||||
|
|
||||||
found, el, stop_re = sub_parse.call(result, element)
|
|
||||||
if !found && element == :strong && @tree.type != :em
|
|
||||||
@src.pos = reset_pos - 1
|
|
||||||
found, el, stop_re = sub_parse.call(type, :em)
|
|
||||||
end
|
|
||||||
if found
|
|
||||||
@src.scan(stop_re)
|
|
||||||
@tree.children << el
|
|
||||||
else
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:emphasis, EMPHASIS_START, '\*|_')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
EOB_MARKER = /^\^\s*?\n/
|
|
||||||
|
|
||||||
# Parse the EOB marker at the current location.
|
|
||||||
def parse_eob_marker
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << new_block_el(:eob)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:eob_marker, EOB_MARKER)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,38 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}#!:|"'\$-])/
|
|
||||||
|
|
||||||
# Parse the backslash-escaped character at the current location.
|
|
||||||
def parse_escaped_chars
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
add_text(@src[1])
|
|
||||||
end
|
|
||||||
define_parser(:escaped_chars, ESCAPED_CHARS, '\\\\')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,102 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/attribute_list'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
def parse_extension_start_tag(type)
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
|
|
||||||
if @src[4] || @src.matched == '{:/}'
|
|
||||||
name = (@src[4] ? "for '#{@src[4]}' " : '')
|
|
||||||
warning("Invalid extension stop tag #{name}found - ignoring it")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ext = @src[1]
|
|
||||||
opts = {}
|
|
||||||
body = nil
|
|
||||||
parse_attribute_list(@src[2] || '', opts)
|
|
||||||
|
|
||||||
if !@src[3]
|
|
||||||
stop_re = (type == :block ? /#{EXT_BLOCK_STOP_STR % ext}/ : /#{EXT_STOP_STR % ext}/)
|
|
||||||
if result = @src.scan_until(stop_re)
|
|
||||||
body = result.sub!(stop_re, '')
|
|
||||||
body.chomp! if type == :block
|
|
||||||
else
|
|
||||||
warning("No stop tag for extension '#{ext}' found - treating it as extension without body")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
handle_extension(ext, opts, body, type)
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_extension(name, opts, body, type)
|
|
||||||
case name
|
|
||||||
when 'comment'
|
|
||||||
@tree.children << Element.new(:comment, body, :category => type) if body.kind_of?(String)
|
|
||||||
when 'nomarkdown'
|
|
||||||
@tree.children << Element.new(:raw, body, :category => type) if body.kind_of?(String)
|
|
||||||
when 'options'
|
|
||||||
opts.select do |k,v|
|
|
||||||
k = k.to_sym
|
|
||||||
if Kramdown::Options.defined?(k)
|
|
||||||
@doc.options[k] = Kramdown::Options.parse(k, v) rescue @doc.options[k]
|
|
||||||
false
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end.each do |k,v|
|
|
||||||
warning("Unknown kramdown option '#{k}'")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
warning("Invalid extension name '#{name}' specified - ignoring extension")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
EXT_STOP_STR = "\\{:/(%s)?\\}"
|
|
||||||
EXT_START_STR = "\\{::(\\w+)(?:\\s(#{ALD_ANY_CHARS}*?)|)(\\/)?\\}"
|
|
||||||
EXT_SPAN_START = /#{EXT_START_STR}|#{EXT_STOP_STR % ALD_ID_NAME}/
|
|
||||||
EXT_BLOCK_START = /^#{OPT_SPACE}(?:#{EXT_START_STR}|#{EXT_STOP_STR % ALD_ID_NAME})\s*?\n/
|
|
||||||
EXT_BLOCK_STOP_STR = "^#{OPT_SPACE}#{EXT_STOP_STR}\s*?\n"
|
|
||||||
|
|
||||||
# Parse the extension block at the current location.
|
|
||||||
def parse_block_extension
|
|
||||||
parse_extension_start_tag(:block)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:block_extension, EXT_BLOCK_START)
|
|
||||||
|
|
||||||
|
|
||||||
# Parse the extension span at the current location.
|
|
||||||
def parse_span_extension
|
|
||||||
parse_extension_start_tag(:span)
|
|
||||||
end
|
|
||||||
define_parser(:span_extension, EXT_SPAN_START, '\{:[:/]')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,73 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/attribute_list'
|
|
||||||
require 'kramdown/parser/kramdown/blank_line'
|
|
||||||
require 'kramdown/parser/kramdown/codeblock'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*)/
|
|
||||||
|
|
||||||
# Parse the foot note definition at the current location.
|
|
||||||
def parse_footnote_definition
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
|
|
||||||
el = Element.new(:footnote_def)
|
|
||||||
parse_blocks(el, @src[2].gsub(INDENT, ''))
|
|
||||||
warning("Duplicate footnote name '#{@src[1]}' - overwriting") if @doc.parse_infos[:footnotes][@src[1]]
|
|
||||||
(@doc.parse_infos[:footnotes][@src[1]] = {})[:content] = el
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:footnote_definition, FOOTNOTE_DEFINITION_START)
|
|
||||||
|
|
||||||
|
|
||||||
FOOTNOTE_MARKER_START = /\[\^(#{ALD_ID_NAME})\]/
|
|
||||||
|
|
||||||
# Parse the footnote marker at the current location.
|
|
||||||
def parse_footnote_marker
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
fn_def = @doc.parse_infos[:footnotes][@src[1]]
|
|
||||||
if fn_def
|
|
||||||
valid = fn_def[:marker] && fn_def[:marker].options[:stack][0..-2].zip(fn_def[:marker].options[:stack][1..-1]).all? do |par, child|
|
|
||||||
par.children.include?(child)
|
|
||||||
end
|
|
||||||
if !fn_def[:marker] || !valid
|
|
||||||
fn_def[:marker] = Element.new(:footnote, nil, :name => @src[1])
|
|
||||||
fn_def[:marker].options[:stack] = [@stack.map {|s| s.first}, @tree, fn_def[:marker]].flatten.compact
|
|
||||||
@tree.children << fn_def[:marker]
|
|
||||||
else
|
|
||||||
warning("Footnote marker '#{@src[1]}' already appeared in document, ignoring newly found marker")
|
|
||||||
add_text(@src.matched)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
warning("Footnote definition for '#{@src[1]}' not found")
|
|
||||||
add_text(@src.matched)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:footnote_marker, FOOTNOTE_MARKER_START, '\[')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,66 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
HEADER_ID=/(?:[ \t]\{#((?:\w|\d)[\w\d-]*)\})?/
|
|
||||||
SETEXT_HEADER_START = /^(#{OPT_SPACE}[^ \t].*?)#{HEADER_ID}[ \t]*?\n(-|=)+\s*?\n/
|
|
||||||
|
|
||||||
# Parse the Setext header at the current location.
|
|
||||||
def parse_setext_header
|
|
||||||
if @tree.children.last && @tree.children.last.type != :blank
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
text, id, level = @src[1].strip, @src[2], @src[3]
|
|
||||||
el = new_block_el(:header, nil, :level => (level == '-' ? 2 : 1), :raw_text => text)
|
|
||||||
add_text(text, el)
|
|
||||||
el.options[:attr] = {'id' => id} if id
|
|
||||||
@tree.children << el
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:setext_header, SETEXT_HEADER_START)
|
|
||||||
|
|
||||||
|
|
||||||
ATX_HEADER_START = /^\#{1,6}/
|
|
||||||
ATX_HEADER_MATCH = /^(\#{1,6})(.+?)\s*?#*#{HEADER_ID}\s*?\n/
|
|
||||||
|
|
||||||
# Parse the Atx header at the current location.
|
|
||||||
def parse_atx_header
|
|
||||||
if @tree.children.last && @tree.children.last.type != :blank
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
result = @src.scan(ATX_HEADER_MATCH)
|
|
||||||
level, text, id = @src[1], @src[2].strip, @src[3]
|
|
||||||
el = new_block_el(:header, nil, :level => level.length, :raw_text => text)
|
|
||||||
add_text(text, el)
|
|
||||||
el.options[:attr] = {'id' => id} if id
|
|
||||||
@tree.children << el
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:atx_header, ATX_HEADER_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,39 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
HR_START = /^#{OPT_SPACE}(\*|-|_)[ \t]*\1[ \t]*\1[ \t]*(\1|[ \t])*\n/
|
|
||||||
|
|
||||||
# Parse the horizontal rule at the current location.
|
|
||||||
def parse_horizontal_rule
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << new_block_el(:hr)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:horizontal_rule, HR_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,173 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
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.options[:attr].delete('markdown'))
|
|
||||||
parse_type = (val == :default ? HTML_PARSE_AS[el.value] : val)
|
|
||||||
end
|
|
||||||
|
|
||||||
@src.scan(/[ \t]*\n/) if parse_type == :block
|
|
||||||
el.options[:outer_element] = true if @tree.type != :html_element
|
|
||||||
el.options[:parent_is_raw] = true if @tree.type == :html_element && @tree.options[:parse_type] == :raw
|
|
||||||
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, :category => :block)
|
|
||||||
@src.scan(/[ \t]*\n/)
|
|
||||||
true
|
|
||||||
elsif result = @src.scan(HTML_INSTRUCTION_RE)
|
|
||||||
@tree.children << Element.new(:xml_pi, result, :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])
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
name = @src[1]
|
|
||||||
|
|
||||||
if @tree.type == :html_element && @tree.value == name
|
|
||||||
throw :stop_block_parsing, :found
|
|
||||||
else
|
|
||||||
warning("Found invalidly used HTML closing tag for '#{name}' - ignoring it")
|
|
||||||
true
|
|
||||||
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, :category => :span)
|
|
||||||
elsif result = @src.scan(HTML_INSTRUCTION_RE)
|
|
||||||
@tree.children << Element.new(:xml_pi, result, :category => :span)
|
|
||||||
elsif result = @src.scan(HTML_TAG_CLOSE_RE)
|
|
||||||
warning("Found invalidly used HTML closing tag for '#{@src[1]}' - ignoring it")
|
|
||||||
elsif result = @src.scan(HTML_TAG_RE)
|
|
||||||
return if HTML_BLOCK_ELEMENTS.include?(@src[1])
|
|
||||||
|
|
||||||
reset_pos = @src.pos
|
|
||||||
attrs = {}
|
|
||||||
@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], :attr => 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
|
|
@ -1,38 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/html'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
# Parse the HTML entity at the current location.
|
|
||||||
def parse_html_entity
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex))
|
|
||||||
end
|
|
||||||
define_parser(:html_entity, Kramdown::Parser::Html::Constants::HTML_ENTITY_RE, '&')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,38 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
LINE_BREAK = /( |\\\\)(?=\n)/
|
|
||||||
|
|
||||||
# Parse the line break at the current location.
|
|
||||||
def parse_line_break
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << Element.new(:br)
|
|
||||||
end
|
|
||||||
define_parser(:line_break, LINE_BREAK, '( |\\\\)(?=\n)')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,156 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
PUNCTUATION_CHARS = "_.:,;!?-"
|
|
||||||
LINK_ID_CHARS = /[a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
|
|
||||||
LINK_ID_NON_CHARS = /[^a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
|
|
||||||
LINK_DEFINITION_START = /^#{OPT_SPACE}\[(#{LINK_ID_CHARS}+)\]:[ \t]*(?:<(.*?)>|([^\s]+))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
|
|
||||||
|
|
||||||
# Parse the link definition at the current location.
|
|
||||||
def parse_link_definition
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
link_id, link_url, link_title = @src[1].downcase, @src[2] || @src[3], @src[5]
|
|
||||||
warning("Duplicate link ID '#{link_id}' - overwriting") if @doc.parse_infos[:link_defs][link_id]
|
|
||||||
@doc.parse_infos[:link_defs][link_id] = [link_url, link_title]
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:link_definition, LINK_DEFINITION_START)
|
|
||||||
|
|
||||||
|
|
||||||
# This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
|
|
||||||
# and the element itself to the <tt>@tree</tt>.
|
|
||||||
def add_link(el, href, title, alt_text = nil)
|
|
||||||
el.options[:attr] ||= {}
|
|
||||||
el.options[:attr]['title'] = title if title
|
|
||||||
if el.type == :a
|
|
||||||
el.options[:attr]['href'] = href
|
|
||||||
else
|
|
||||||
el.options[:attr]['src'] = href
|
|
||||||
el.options[:attr]['alt'] = alt_text
|
|
||||||
el.children.clear
|
|
||||||
end
|
|
||||||
@tree.children << el
|
|
||||||
end
|
|
||||||
|
|
||||||
LINK_TEXT_BRACKET_RE = /\\\[|\\\]|\[|\]/
|
|
||||||
LINK_INLINE_ID_RE = /\s*?\[(#{LINK_ID_CHARS}+)?\]/
|
|
||||||
LINK_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/
|
|
||||||
LINK_START = /!?\[(?=[^^])/
|
|
||||||
|
|
||||||
# Parse the link at the current scanner position. This method is used to parse normal links as
|
|
||||||
# well as image links.
|
|
||||||
def parse_link
|
|
||||||
result = @src.scan(LINK_START)
|
|
||||||
reset_pos = @src.pos
|
|
||||||
|
|
||||||
link_type = (result =~ /^!/ ? :img : :a)
|
|
||||||
|
|
||||||
# no nested links allowed
|
|
||||||
if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? {|t,s| t && (t.type == :img || t.type == :a)})
|
|
||||||
add_text(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
el = Element.new(link_type)
|
|
||||||
|
|
||||||
stop_re = /\]|!?\[/
|
|
||||||
count = 1
|
|
||||||
found = parse_spans(el, stop_re) do
|
|
||||||
case @src.matched
|
|
||||||
when "[", "!["
|
|
||||||
count += 1
|
|
||||||
when "]"
|
|
||||||
count -= 1
|
|
||||||
end
|
|
||||||
count - el.children.select {|c| c.type == :img}.size == 0
|
|
||||||
end
|
|
||||||
if !found || (link_type == :a && el.children.empty?)
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
alt_text = extract_string(reset_pos...@src.pos, @src)
|
|
||||||
conv_link_id = alt_text.gsub(/(\s|\n)+/m, ' ').gsub(LINK_ID_NON_CHARS, '').downcase
|
|
||||||
@src.scan(stop_re)
|
|
||||||
|
|
||||||
# reference style link or no link url
|
|
||||||
if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
|
|
||||||
link_id = (@src[1] || conv_link_id).downcase
|
|
||||||
if link_id.empty?
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
elsif @doc.parse_infos[:link_defs].has_key?(link_id)
|
|
||||||
add_link(el, @doc.parse_infos[:link_defs][link_id].first, @doc.parse_infos[:link_defs][link_id].last, alt_text)
|
|
||||||
else
|
|
||||||
warning("No link definition for link ID '#{link_id}' found")
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
# link url in parentheses
|
|
||||||
if @src.scan(/\(<(.*?)>/)
|
|
||||||
link_url = @src[1]
|
|
||||||
if @src.scan(/\)/)
|
|
||||||
add_link(el, link_url, nil, alt_text)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
link_url = ''
|
|
||||||
re = /\(|\)|\s/
|
|
||||||
nr_of_brackets = 0
|
|
||||||
while temp = @src.scan_until(re)
|
|
||||||
link_url += temp
|
|
||||||
case @src.matched
|
|
||||||
when /\s/
|
|
||||||
break
|
|
||||||
when '('
|
|
||||||
nr_of_brackets += 1
|
|
||||||
when ')'
|
|
||||||
nr_of_brackets -= 1
|
|
||||||
break if nr_of_brackets == 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
link_url = link_url[1..-2]
|
|
||||||
|
|
||||||
if nr_of_brackets == 0
|
|
||||||
add_link(el, link_url, nil, alt_text)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @src.scan(LINK_INLINE_TITLE_RE)
|
|
||||||
add_link(el, link_url, @src[2], alt_text)
|
|
||||||
else
|
|
||||||
@src.pos = reset_pos
|
|
||||||
add_text(result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:link, LINK_START, '!?\[')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,232 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/blank_line'
|
|
||||||
require 'kramdown/parser/kramdown/eob'
|
|
||||||
require 'kramdown/parser/kramdown/horizontal_rule'
|
|
||||||
require 'kramdown/parser/kramdown/attribute_list'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
# Used for parsing the first line of a list item or a definition, i.e. the line with list item
|
|
||||||
# marker or the definition marker.
|
|
||||||
def parse_first_list_line(indentation, content)
|
|
||||||
if content =~ /^\s*(#{IAL_SPAN_START})?\s*\n/
|
|
||||||
indentation = 4
|
|
||||||
else
|
|
||||||
while content =~ /^ *\t/
|
|
||||||
temp = content.scan(/^ */).first.length + indentation
|
|
||||||
content.sub!(/^( *)(\t+)/) {$1 + " "*(4 - (temp % 4)) + " "*($2.length - 1)*4}
|
|
||||||
end
|
|
||||||
indentation += content.scan(/^ */).first.length
|
|
||||||
end
|
|
||||||
content.sub!(/^\s*/, '')
|
|
||||||
|
|
||||||
indent_re = /^ {#{indentation}}/
|
|
||||||
content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*?\n/
|
|
||||||
[content, indentation, content_re, indent_re]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
LIST_START_UL = /^(#{OPT_SPACE}[+*-])([\t| ].*?\n)/
|
|
||||||
LIST_START_OL = /^(#{OPT_SPACE}\d+\.)([\t| ].*?\n)/
|
|
||||||
LIST_START = /#{LIST_START_UL}|#{LIST_START_OL}/
|
|
||||||
|
|
||||||
# Parse the ordered or unordered list at the current location.
|
|
||||||
def parse_list
|
|
||||||
if @tree.children.last && @tree.children.last.type == :p # last element must not be a paragraph
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
|
|
||||||
list = new_block_el(type)
|
|
||||||
|
|
||||||
item = nil
|
|
||||||
indent_re = nil
|
|
||||||
content_re = nil
|
|
||||||
eob_found = false
|
|
||||||
nested_list_found = false
|
|
||||||
while !@src.eos?
|
|
||||||
if @src.check(HR_START)
|
|
||||||
break
|
|
||||||
elsif @src.scan(list_start_re)
|
|
||||||
item = Element.new(:li)
|
|
||||||
item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
|
|
||||||
list.children << item
|
|
||||||
|
|
||||||
item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match|
|
|
||||||
parse_attribute_list($~[1], item.options[:ial] ||= {})
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
|
|
||||||
/^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
|
|
||||||
nested_list_found = false
|
|
||||||
elsif result = @src.scan(content_re)
|
|
||||||
result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) }
|
|
||||||
result.sub!(indent_re, '')
|
|
||||||
if !nested_list_found && result =~ LIST_START
|
|
||||||
parse_blocks(item, item.value)
|
|
||||||
if item.children.length == 1 && item.children.first.type == :p
|
|
||||||
item.value = ''
|
|
||||||
else
|
|
||||||
item.children.clear
|
|
||||||
end
|
|
||||||
nested_list_found = true
|
|
||||||
end
|
|
||||||
item.value << result
|
|
||||||
elsif result = @src.scan(BLANK_LINE)
|
|
||||||
nested_list_found = true
|
|
||||||
item.value << result
|
|
||||||
elsif @src.scan(EOB_MARKER)
|
|
||||||
eob_found = true
|
|
||||||
break
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree.children << list
|
|
||||||
|
|
||||||
last = nil
|
|
||||||
list.children.each do |it|
|
|
||||||
temp = Element.new(:temp)
|
|
||||||
parse_blocks(temp, it.value)
|
|
||||||
it.children += temp.children
|
|
||||||
it.value = nil
|
|
||||||
next if it.children.size == 0
|
|
||||||
|
|
||||||
if it.children.first.type == :p && (it.children.length < 2 || it.children[1].type != :blank ||
|
|
||||||
(it == list.children.last && it.children.length == 2 && !eob_found)) &&
|
|
||||||
(list.children.last != it || list.children.size == 1 ||
|
|
||||||
list.children[0..-2].any? {|cit| cit.children.first.type != :p || cit.children.first.options[:transparent]})
|
|
||||||
it.children.first.children.first.value += "\n" if it.children.size > 1 && it.children[1].type != :blank
|
|
||||||
it.children.first.options[:transparent] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if it.children.last.type == :blank
|
|
||||||
last = it.children.pop
|
|
||||||
else
|
|
||||||
last = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree.children << last if !last.nil? && !eob_found
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:list, LIST_START)
|
|
||||||
|
|
||||||
|
|
||||||
DEFINITION_LIST_START = /^(#{OPT_SPACE}:)([\t| ].*?\n)/
|
|
||||||
|
|
||||||
# Parse the ordered or unordered list at the current location.
|
|
||||||
def parse_definition_list
|
|
||||||
children = @tree.children
|
|
||||||
if !children.last || (children.length == 1 && children.last.type != :p ) ||
|
|
||||||
(children.length >= 2 && children[-1].type != :p && (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
first_as_para = false
|
|
||||||
deflist = new_block_el(:dl)
|
|
||||||
para = @tree.children.pop
|
|
||||||
if para.type == :blank
|
|
||||||
para = @tree.children.pop
|
|
||||||
first_as_para = true
|
|
||||||
end
|
|
||||||
para.children.first.value.split("\n").each do |term|
|
|
||||||
el = Element.new(:dt)
|
|
||||||
el.children << Element.new(:raw_text, term)
|
|
||||||
deflist.children << el
|
|
||||||
end
|
|
||||||
|
|
||||||
item = nil
|
|
||||||
indent_re = nil
|
|
||||||
content_re = nil
|
|
||||||
def_start_re = DEFINITION_LIST_START
|
|
||||||
while !@src.eos?
|
|
||||||
if @src.scan(def_start_re)
|
|
||||||
item = Element.new(:dd)
|
|
||||||
item.options[:first_as_para] = first_as_para
|
|
||||||
item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
|
|
||||||
deflist.children << item
|
|
||||||
|
|
||||||
item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match|
|
|
||||||
parse_attribute_list($~[1], item.options[:ial] ||= {})
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/
|
|
||||||
first_as_para = false
|
|
||||||
elsif result = @src.scan(content_re)
|
|
||||||
result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) }
|
|
||||||
result.sub!(indent_re, '')
|
|
||||||
item.value << result
|
|
||||||
first_as_para = false
|
|
||||||
elsif result = @src.scan(BLANK_LINE)
|
|
||||||
first_as_para = true
|
|
||||||
item.value << result
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
last = nil
|
|
||||||
deflist.children.each do |it|
|
|
||||||
next if it.type == :dt
|
|
||||||
|
|
||||||
parse_blocks(it, it.value)
|
|
||||||
it.value = nil
|
|
||||||
next if it.children.size == 0
|
|
||||||
|
|
||||||
if it.children.last.type == :blank
|
|
||||||
last = it.children.pop
|
|
||||||
else
|
|
||||||
last = nil
|
|
||||||
end
|
|
||||||
if it.children.first.type == :p && !it.options.delete(:first_as_para)
|
|
||||||
it.children.first.children.first.value += "\n" if it.children.size > 1
|
|
||||||
it.children.first.options[:transparent] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @tree.children.length >= 1 && @tree.children.last.type == :dl
|
|
||||||
@tree.children[-1].children += deflist.children
|
|
||||||
elsif @tree.children.length >= 2 && @tree.children[-1].type == :blank && @tree.children[-2].type == :dl
|
|
||||||
@tree.children.pop
|
|
||||||
@tree.children[-1].children += deflist.children
|
|
||||||
else
|
|
||||||
@tree.children << deflist
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree.children << last if !last.nil?
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:definition_list, DEFINITION_LIST_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,53 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
BLOCK_MATH_START = /^#{OPT_SPACE}(\\)?\$\$(.*?)\$\$\s*?\n/m
|
|
||||||
|
|
||||||
# Parse the math block at the current location.
|
|
||||||
def parse_block_math
|
|
||||||
if @src[1]
|
|
||||||
@src.scan(/^#{OPT_SPACE}\\/)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << new_block_el(:math, @src[2], :category => :block)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:block_math, BLOCK_MATH_START)
|
|
||||||
|
|
||||||
|
|
||||||
INLINE_MATH_START = /\$\$(.*?)\$\$/
|
|
||||||
|
|
||||||
# Parse the inline math at the current location.
|
|
||||||
def parse_inline_math
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
@tree.children << Element.new(:math, @src[1], :category => :span)
|
|
||||||
end
|
|
||||||
define_parser(:inline_math, INLINE_MATH_START, '\$')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,44 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
PARAGRAPH_START = /^#{OPT_SPACE}[^ \t].*?\n/
|
|
||||||
|
|
||||||
# Parse the paragraph at the current location.
|
|
||||||
def parse_paragraph
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
if @tree.children.last && @tree.children.last.type == :p
|
|
||||||
@tree.children.last.children.first.value << "\n" << @src.matched.chomp
|
|
||||||
else
|
|
||||||
@tree.children << new_block_el(:p)
|
|
||||||
add_text(@src.matched.lstrip.chomp, @tree.children.last)
|
|
||||||
end
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:paragraph, PARAGRAPH_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,214 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Parts of this file are based on code from Maruku by Andrea Censi.
|
|
||||||
# The needed license statements follow:
|
|
||||||
#
|
|
||||||
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
|
||||||
#
|
|
||||||
# Maruku 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 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Maruku 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 Maruku; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
#
|
|
||||||
# NOTA BENE:
|
|
||||||
#
|
|
||||||
# The following algorithm is a rip-off of RubyPants written by
|
|
||||||
# Christian Neukirchen.
|
|
||||||
#
|
|
||||||
# RubyPants is a Ruby port of SmartyPants written by John Gruber.
|
|
||||||
#
|
|
||||||
# This file is distributed under the GPL, which I guess is compatible
|
|
||||||
# with the terms of the RubyPants license.
|
|
||||||
#
|
|
||||||
# -- Andrea Censi
|
|
||||||
#
|
|
||||||
# = RubyPants -- SmartyPants ported to Ruby
|
|
||||||
#
|
|
||||||
# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
|
|
||||||
# Copyright (C) 2004 Christian Neukirchen
|
|
||||||
#
|
|
||||||
# Incooporates ideas, comments and documentation by Chad Miller
|
|
||||||
# Copyright (C) 2004 Chad Miller
|
|
||||||
#
|
|
||||||
# Original SmartyPants by John Gruber
|
|
||||||
# Copyright (C) 2003 John Gruber
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# = RubyPants -- SmartyPants ported to Ruby
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# [snip]
|
|
||||||
#
|
|
||||||
# == Authors
|
|
||||||
#
|
|
||||||
# John Gruber did all of the hard work of writing this software in
|
|
||||||
# Perl for Movable Type and almost all of this useful documentation.
|
|
||||||
# Chad Miller ported it to Python to use with Pyblosxom.
|
|
||||||
#
|
|
||||||
# Christian Neukirchen provided the Ruby port, as a general-purpose
|
|
||||||
# library that follows the *Cloth API.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# == Copyright and License
|
|
||||||
#
|
|
||||||
# === SmartyPants license:
|
|
||||||
#
|
|
||||||
# Copyright (c) 2003 John Gruber
|
|
||||||
# (http://daringfireball.net)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# * Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in
|
|
||||||
# the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
#
|
|
||||||
# * Neither the name "SmartyPants" nor the names of its contributors
|
|
||||||
# may be used to endorse or promote products derived from this
|
|
||||||
# software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# This software is provided by the copyright holders and contributors
|
|
||||||
# "as is" and any express or implied warranties, including, but not
|
|
||||||
# limited to, the implied warranties of merchantability and fitness
|
|
||||||
# for a particular purpose are disclaimed. In no event shall the
|
|
||||||
# copyright owner or contributors be liable for any direct, indirect,
|
|
||||||
# incidental, special, exemplary, or consequential damages (including,
|
|
||||||
# but not limited to, procurement of substitute goods or services;
|
|
||||||
# loss of use, data, or profits; or business interruption) however
|
|
||||||
# caused and on any theory of liability, whether in contract, strict
|
|
||||||
# liability, or tort (including negligence or otherwise) arising in
|
|
||||||
# any way out of the use of this software, even if advised of the
|
|
||||||
# possibility of such damage.
|
|
||||||
#
|
|
||||||
# === RubyPants license
|
|
||||||
#
|
|
||||||
# RubyPants is a derivative work of SmartyPants and smartypants.py.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# * Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in
|
|
||||||
# the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
#
|
|
||||||
# This software is provided by the copyright holders and contributors
|
|
||||||
# "as is" and any express or implied warranties, including, but not
|
|
||||||
# limited to, the implied warranties of merchantability and fitness
|
|
||||||
# for a particular purpose are disclaimed. In no event shall the
|
|
||||||
# copyright owner or contributors be liable for any direct, indirect,
|
|
||||||
# incidental, special, exemplary, or consequential damages (including,
|
|
||||||
# but not limited to, procurement of substitute goods or services;
|
|
||||||
# loss of use, data, or profits; or business interruption) however
|
|
||||||
# caused and on any theory of liability, whether in contract, strict
|
|
||||||
# liability, or tort (including negligence or otherwise) arising in
|
|
||||||
# any way out of the use of this software, even if advised of the
|
|
||||||
# possibility of such damage.
|
|
||||||
#
|
|
||||||
# == Links
|
|
||||||
#
|
|
||||||
# John Gruber:: http://daringfireball.net
|
|
||||||
# SmartyPants:: http://daringfireball.net/projects/smartypants
|
|
||||||
#
|
|
||||||
# Chad Miller:: http://web.chad.org
|
|
||||||
#
|
|
||||||
# Christian Neukirchen:: http://kronavita.de/chris
|
|
||||||
#
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
SQ_PUNCT = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
|
|
||||||
SQ_CLOSE = %![^\ \\\\\t\r\n\\[{(-]!
|
|
||||||
|
|
||||||
SQ_RULES = [
|
|
||||||
[/("|')(?=#{SQ_PUNCT}\B)/, [:rquote1]],
|
|
||||||
# Special case for double sets of quotes, e.g.:
|
|
||||||
# <p>He said, "'Quoted' words in a larger quote."</p>
|
|
||||||
[/(\s?)"'(?=\w)/, [1, :ldquo, :lsquo]],
|
|
||||||
[/(\s?)'"(?=\w)/, [1, :lsquo, :ldquo]],
|
|
||||||
# Special case for decade abbreviations (the '80s):
|
|
||||||
[/(\s?)'(?=\d\ds)/, [1, :rsquo]],
|
|
||||||
|
|
||||||
# Get most opening single/double quotes:
|
|
||||||
[/(\s)('|")(?=\w)/, [1, :lquote2]],
|
|
||||||
# Single/double closing quotes:
|
|
||||||
[/(#{SQ_CLOSE})('|")/, [1, :rquote2]],
|
|
||||||
# Special case for e.g. "<i>Custer</i>'s Last Stand."
|
|
||||||
[/("|')(\s|s\b|$)/, [:rquote1, 2]],
|
|
||||||
# Any remaining single quotes should be opening ones:
|
|
||||||
[/(.?)'/m, [1, :lsquo]],
|
|
||||||
[/(.?)"/m, [1, :ldquo]],
|
|
||||||
] #'"
|
|
||||||
|
|
||||||
SQ_SUBSTS = {
|
|
||||||
[:rquote1, '"'] => :rdquo,
|
|
||||||
[:rquote1, "'"] => :rsquo,
|
|
||||||
[:rquote2, '"'] => :rdquo,
|
|
||||||
[:rquote2, "'"] => :rsquo,
|
|
||||||
[:lquote1, '"'] => :ldquo,
|
|
||||||
[:lquote1, "'"] => :lsquo,
|
|
||||||
[:lquote2, '"'] => :ldquo,
|
|
||||||
[:lquote2, "'"] => :lsquo,
|
|
||||||
}
|
|
||||||
SMART_QUOTES_RE = /[^\\]?["']/
|
|
||||||
|
|
||||||
# Parse the smart quotes at current location.
|
|
||||||
def parse_smart_quotes
|
|
||||||
regexp, substs = SQ_RULES.find {|reg, subst| @src.scan(reg)}
|
|
||||||
substs.each do |subst|
|
|
||||||
if subst.kind_of?(Integer)
|
|
||||||
add_text(@src[subst].to_s)
|
|
||||||
else
|
|
||||||
val = SQ_SUBSTS[[subst, @src[subst.to_s[-1,1].to_i]]] || subst
|
|
||||||
@tree.children << Element.new(:smart_quote, val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:smart_quotes, SMART_QUOTES_RE, '[^\\\\]?["\']')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,126 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
require 'kramdown/parser/kramdown/blank_line'
|
|
||||||
require 'kramdown/parser/kramdown/eob'
|
|
||||||
require 'kramdown/parser/kramdown/horizontal_rule'
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
TABLE_SEP_LINE = /^#{OPT_SPACE}(?:\||\+)([ ]?:?-[+|: -]*)[ \t]*\n/
|
|
||||||
TABLE_HSEP_ALIGN = /[ ]?(:?)-+(:?)[ ]?/
|
|
||||||
TABLE_FSEP_LINE = /^#{OPT_SPACE}(\||\+)[ ]?:?=[+|: =]*[ \t]*\n/
|
|
||||||
TABLE_ROW_LINE = /^#{OPT_SPACE}\|(.*?)[ \t]*\n/
|
|
||||||
TABLE_START = /^#{OPT_SPACE}\|(?:-|(?!=))/
|
|
||||||
|
|
||||||
# Parse the table at the current location.
|
|
||||||
def parse_table
|
|
||||||
orig_pos = @src.pos
|
|
||||||
table = new_block_el(:table, nil, :alignment => [])
|
|
||||||
|
|
||||||
@src.scan(TABLE_SEP_LINE)
|
|
||||||
|
|
||||||
rows = []
|
|
||||||
has_footer = false
|
|
||||||
columns = 0
|
|
||||||
|
|
||||||
add_container = lambda do |type, force|
|
|
||||||
if force || type != :tbody || !has_footer
|
|
||||||
cont = Element.new(type)
|
|
||||||
cont.children, rows = rows, []
|
|
||||||
table.children << cont
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while !@src.eos?
|
|
||||||
if @src.scan(TABLE_SEP_LINE) && !rows.empty?
|
|
||||||
if table.options[:alignment].empty? && !has_footer
|
|
||||||
add_container.call(:thead, false)
|
|
||||||
table.options[:alignment] = @src[1].scan(TABLE_HSEP_ALIGN).map do |left, right|
|
|
||||||
(left.empty? && right.empty? && :default) || (right.empty? && :left) || (left.empty? && :right) || :center
|
|
||||||
end
|
|
||||||
else # treat as normal separator line
|
|
||||||
add_container.call(:tbody, false)
|
|
||||||
end
|
|
||||||
elsif @src.scan(TABLE_FSEP_LINE)
|
|
||||||
add_container.call(:tbody, true) if !rows.empty?
|
|
||||||
has_footer = true
|
|
||||||
elsif @src.scan(TABLE_ROW_LINE)
|
|
||||||
trow = Element.new(:tr)
|
|
||||||
cells = (@src[1] + ' ').split(/\|/)
|
|
||||||
i = 0
|
|
||||||
while i < cells.length - 1
|
|
||||||
backslashes = cells[i].scan(/\\+$/).first
|
|
||||||
if backslashes && backslashes.length % 2 == 1
|
|
||||||
cells[i] = cells[i].chop + '|' + cells[i+1]
|
|
||||||
cells.delete_at(i+1)
|
|
||||||
else
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
cells.pop if cells.last.strip.empty?
|
|
||||||
cells.each do |cell_text|
|
|
||||||
tcell = Element.new(:td)
|
|
||||||
tcell.children << Element.new(:raw_text, cell_text.strip)
|
|
||||||
trow.children << tcell
|
|
||||||
end
|
|
||||||
columns = [columns, cells.length].max
|
|
||||||
rows << trow
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
add_container.call(has_footer ? :tfoot : :tbody, false) if !rows.empty?
|
|
||||||
|
|
||||||
if !table.children.any? {|c| c.type == :tbody}
|
|
||||||
warning("Found table without body - ignoring it")
|
|
||||||
@src.pos = orig_pos
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
# adjust all table rows to have equal number of columns, same for alignment defs
|
|
||||||
table.children.each do |kind|
|
|
||||||
kind.children.each do |row|
|
|
||||||
(columns - row.children.length).times do
|
|
||||||
row.children << Element.new(:td)
|
|
||||||
end
|
|
||||||
row.children.each {|el| el.type = :th} if kind.type == :thead
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if table.options[:alignment].length > columns
|
|
||||||
table.options[:alignment] = table.options[:alignment][0...columns]
|
|
||||||
else
|
|
||||||
table.options[:alignment] += [:default] * (columns - table.options[:alignment].length)
|
|
||||||
end
|
|
||||||
|
|
||||||
@tree.children << table
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
define_parser(:table, TABLE_START)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,52 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
module Parser
|
|
||||||
class Kramdown
|
|
||||||
|
|
||||||
TYPOGRAPHIC_SYMS = [['---', :mdash], ['--', :ndash], ['...', :hellip],
|
|
||||||
['\\<<', '<<'], ['\\>>', '>>'],
|
|
||||||
['<< ', :laquo_space], [' >>', :raquo_space],
|
|
||||||
['<<', :laquo], ['>>', :raquo]]
|
|
||||||
TYPOGRAPHIC_SYMS_SUBST = Hash[*TYPOGRAPHIC_SYMS.flatten]
|
|
||||||
TYPOGRAPHIC_SYMS_RE = /#{TYPOGRAPHIC_SYMS.map {|k,v| Regexp.escape(k)}.join('|')}/
|
|
||||||
|
|
||||||
# Parse the typographic symbols at the current location.
|
|
||||||
def parse_typographic_syms
|
|
||||||
@src.pos += @src.matched_size
|
|
||||||
val = TYPOGRAPHIC_SYMS_SUBST[@src.matched]
|
|
||||||
if val.kind_of?(Symbol)
|
|
||||||
@tree.children << Element.new(:typographic_sym, val)
|
|
||||||
elsif @src.matched == '\\<<'
|
|
||||||
@tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'))
|
|
||||||
@tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('lt'))
|
|
||||||
else
|
|
||||||
@tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'))
|
|
||||||
@tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('gt'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
define_parser(:typographic_syms, TYPOGRAPHIC_SYMS_RE, '--|\\.\\.\\.|(?:\\\\| )?(?:<<|>>)')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,36 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# == Utils Module
|
|
||||||
#
|
|
||||||
# This module contains utility class/modules/methods that can be used by both parsers and
|
|
||||||
# converters.
|
|
||||||
module Utils
|
|
||||||
|
|
||||||
autoload :Entities, 'kramdown/utils/entities'
|
|
||||||
autoload :HTML, 'kramdown/utils/html'
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,338 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Utils
|
|
||||||
|
|
||||||
module Entities
|
|
||||||
|
|
||||||
class Entity < Struct.new(:code_point, :name)
|
|
||||||
|
|
||||||
def char
|
|
||||||
[code_point].pack('U*') rescue nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
ENTITY_TABLE = [
|
|
||||||
[913, 'Alpha'],
|
|
||||||
[914, 'Beta'],
|
|
||||||
[915, 'Gamma'],
|
|
||||||
[916, 'Delta'],
|
|
||||||
[917, 'Epsilon'],
|
|
||||||
[918, 'Zeta'],
|
|
||||||
[919, 'Eta'],
|
|
||||||
[920, 'Theta'],
|
|
||||||
[921, 'Iota'],
|
|
||||||
[922, 'Kappa'],
|
|
||||||
[923, 'Lambda'],
|
|
||||||
[924, 'Mu'],
|
|
||||||
[925, 'Nu'],
|
|
||||||
[926, 'Xi'],
|
|
||||||
[927, 'Omicron'],
|
|
||||||
[928, 'Pi'],
|
|
||||||
[929, 'Rho'],
|
|
||||||
[931, 'Sigma'],
|
|
||||||
[932, 'Tau'],
|
|
||||||
[933, 'Upsilon'],
|
|
||||||
[934, 'Phi'],
|
|
||||||
[935, 'Chi'],
|
|
||||||
[936, 'Psi'],
|
|
||||||
[937, 'Omega'],
|
|
||||||
[945, 'alpha'],
|
|
||||||
[946, 'beta'],
|
|
||||||
[947, 'gamma'],
|
|
||||||
[948, 'delta'],
|
|
||||||
[949, 'epsilon'],
|
|
||||||
[950, 'zeta'],
|
|
||||||
[951, 'eta'],
|
|
||||||
[952, 'theta'],
|
|
||||||
[953, 'iota'],
|
|
||||||
[954, 'kappa'],
|
|
||||||
[955, 'lambda'],
|
|
||||||
[956, 'mu'],
|
|
||||||
[957, 'nu'],
|
|
||||||
[958, 'xi'],
|
|
||||||
[959, 'omicron'],
|
|
||||||
[960, 'pi'],
|
|
||||||
[961, 'rho'],
|
|
||||||
[963, 'sigma'],
|
|
||||||
[964, 'tau'],
|
|
||||||
[965, 'upsilon'],
|
|
||||||
[966, 'phi'],
|
|
||||||
[967, 'chi'],
|
|
||||||
[968, 'psi'],
|
|
||||||
[969, 'omega'],
|
|
||||||
[962, 'sigmaf'],
|
|
||||||
[977, 'thetasym'],
|
|
||||||
[982, 'piv'],
|
|
||||||
[8230, 'hellip'],
|
|
||||||
[8242, 'prime'],
|
|
||||||
[8254, 'oline'],
|
|
||||||
[8260, 'frasl'],
|
|
||||||
[8472, 'weierp'],
|
|
||||||
[8465, 'image'],
|
|
||||||
[8476, 'real'],
|
|
||||||
[8501, 'alefsym'],
|
|
||||||
[8226, 'bull'],
|
|
||||||
[8482, 'trade'],
|
|
||||||
[8592, 'larr'],
|
|
||||||
[8594, 'rarr'],
|
|
||||||
[8593, 'uarr'],
|
|
||||||
[8595, 'darr'],
|
|
||||||
[8596, 'harr'],
|
|
||||||
[8629, 'crarr'],
|
|
||||||
[8657, 'uArr'],
|
|
||||||
[8659, 'dArr'],
|
|
||||||
[8656, 'lArr'],
|
|
||||||
[8658, 'rArr'],
|
|
||||||
[8660, 'hArr'],
|
|
||||||
[8704, 'forall'],
|
|
||||||
[8706, 'part'],
|
|
||||||
[8707, 'exist'],
|
|
||||||
[8709, 'empty'],
|
|
||||||
[8711, 'nabla'],
|
|
||||||
[8712, 'isin'],
|
|
||||||
[8715, 'ni'],
|
|
||||||
[8713, 'notin'],
|
|
||||||
[8721, 'sum'],
|
|
||||||
[8719, 'prod'],
|
|
||||||
[8722, 'minus'],
|
|
||||||
[8727, 'lowast'],
|
|
||||||
[8730, 'radic'],
|
|
||||||
[8733, 'prop'],
|
|
||||||
[8734, 'infin'],
|
|
||||||
[8736, 'ang'],
|
|
||||||
[8743, 'and'],
|
|
||||||
[8744, 'or'],
|
|
||||||
[8745, 'cup'],
|
|
||||||
[8746, 'cap'],
|
|
||||||
[8747, 'int'],
|
|
||||||
[8756, 'there4'],
|
|
||||||
[8764, 'sim'],
|
|
||||||
[8776, 'asymp'],
|
|
||||||
[8773, 'cong'],
|
|
||||||
[8800, 'ne'],
|
|
||||||
[8801, 'equiv'],
|
|
||||||
[8804, 'le'],
|
|
||||||
[8805, 'ge'],
|
|
||||||
[8834, 'sub'],
|
|
||||||
[8835, 'sup'],
|
|
||||||
[8838, 'sube'],
|
|
||||||
[8839, 'supe'],
|
|
||||||
[8836, 'nsub'],
|
|
||||||
[8853, 'oplus'],
|
|
||||||
[8855, 'otimes'],
|
|
||||||
[8869, 'perp'],
|
|
||||||
[8901, 'sdot'],
|
|
||||||
[8968, 'rceil'],
|
|
||||||
[8969, 'lceil'],
|
|
||||||
[8970, 'lfloor'],
|
|
||||||
[8971, 'rfloor'],
|
|
||||||
[9001, 'rang'],
|
|
||||||
[9002, 'lang'],
|
|
||||||
[9674, 'loz'],
|
|
||||||
[9824, 'spades'],
|
|
||||||
[9827, 'clubs'],
|
|
||||||
[9829, 'hearts'],
|
|
||||||
[9830, 'diams'],
|
|
||||||
[38, 'amp'],
|
|
||||||
[34, 'quot'],
|
|
||||||
[39, 'apos'],
|
|
||||||
[169, 'copy'],
|
|
||||||
[60, 'lt'],
|
|
||||||
[62, 'gt'],
|
|
||||||
[338, 'OElig'],
|
|
||||||
[339, 'oelig'],
|
|
||||||
[352, 'Scaron'],
|
|
||||||
[353, 'scaron'],
|
|
||||||
[376, 'Yuml'],
|
|
||||||
[710, 'circ'],
|
|
||||||
[732, 'tilde'],
|
|
||||||
[8211, 'ndash'],
|
|
||||||
[8212, 'mdash'],
|
|
||||||
[8216, 'lsquo'],
|
|
||||||
[8217, 'rsquo'],
|
|
||||||
[8220, 'ldquo'],
|
|
||||||
[8221, 'rdquo'],
|
|
||||||
[8224, 'dagger'],
|
|
||||||
[8225, 'Dagger'],
|
|
||||||
[8240, 'permil'],
|
|
||||||
[8364, 'euro'],
|
|
||||||
[8249, 'lsaquo'],
|
|
||||||
[8250, 'rsaquo'],
|
|
||||||
[160, 'nbsp'],
|
|
||||||
[161, 'iexcl'],
|
|
||||||
[163, 'pound'],
|
|
||||||
[164, 'curren'],
|
|
||||||
[165, 'yen'],
|
|
||||||
[166, 'brvbar'],
|
|
||||||
[167, 'sect'],
|
|
||||||
[171, 'laquo'],
|
|
||||||
[187, 'raquo'],
|
|
||||||
[174, 'reg'],
|
|
||||||
[170, 'ordf'],
|
|
||||||
[172, 'not'],
|
|
||||||
[176, 'deg'],
|
|
||||||
[177, 'plusmn'],
|
|
||||||
[180, 'acute'],
|
|
||||||
[181, 'micro'],
|
|
||||||
[182, 'para'],
|
|
||||||
[183, 'middot'],
|
|
||||||
[186, 'ordm'],
|
|
||||||
[162, 'cent'],
|
|
||||||
[185, 'sup1'],
|
|
||||||
[178, 'sup2'],
|
|
||||||
[179, 'sup3'],
|
|
||||||
[189, 'frac12'],
|
|
||||||
[188, 'frac14'],
|
|
||||||
[190, 'frac34'],
|
|
||||||
[192, 'Agrave'],
|
|
||||||
[193, 'Aacute'],
|
|
||||||
[194, 'Acirc'],
|
|
||||||
[195, 'Atilde'],
|
|
||||||
[196, 'Auml'],
|
|
||||||
[197, 'Aring'],
|
|
||||||
[198, 'AElig'],
|
|
||||||
[199, 'Ccedil'],
|
|
||||||
[200, 'Egrave'],
|
|
||||||
[201, 'Eacute'],
|
|
||||||
[202, 'Ecirc'],
|
|
||||||
[203, 'Euml'],
|
|
||||||
[204, 'Igrave'],
|
|
||||||
[205, 'Iacute'],
|
|
||||||
[206, 'Icirc'],
|
|
||||||
[207, 'Iuml'],
|
|
||||||
[208, 'ETH'],
|
|
||||||
[209, 'Ntilde'],
|
|
||||||
[210, 'Ograve'],
|
|
||||||
[211, 'Oacute'],
|
|
||||||
[212, 'Ocirc'],
|
|
||||||
[213, 'Otilde'],
|
|
||||||
[214, 'Ouml'],
|
|
||||||
[215, 'times'],
|
|
||||||
[216, 'Oslash'],
|
|
||||||
[217, 'Ugrave'],
|
|
||||||
[218, 'Uacute'],
|
|
||||||
[219, 'Ucirc'],
|
|
||||||
[220, 'Uuml'],
|
|
||||||
[221, 'Yacute'],
|
|
||||||
[222, 'THORN'],
|
|
||||||
[223, 'szlig'],
|
|
||||||
[224, 'agrave'],
|
|
||||||
[225, 'aacute'],
|
|
||||||
[226, 'acirc'],
|
|
||||||
[227, 'atilde'],
|
|
||||||
[228, 'auml'],
|
|
||||||
[229, 'aring'],
|
|
||||||
[230, 'aelig'],
|
|
||||||
[231, 'ccedil'],
|
|
||||||
[232, 'egrave'],
|
|
||||||
[233, 'eacute'],
|
|
||||||
[234, 'ecirc'],
|
|
||||||
[235, 'euml'],
|
|
||||||
[236, 'igrave'],
|
|
||||||
[237, 'iacute'],
|
|
||||||
[238, 'icirc'],
|
|
||||||
[239, 'iuml'],
|
|
||||||
[240, 'eth'],
|
|
||||||
[241, 'ntilde'],
|
|
||||||
[242, 'ograve'],
|
|
||||||
[243, 'oacute'],
|
|
||||||
[244, 'ocirc'],
|
|
||||||
[245, 'otilde'],
|
|
||||||
[246, 'ouml'],
|
|
||||||
[247, 'divide'],
|
|
||||||
[248, 'oslash'],
|
|
||||||
[249, 'ugrave'],
|
|
||||||
[250, 'uacute'],
|
|
||||||
[251, 'ucirc'],
|
|
||||||
[252, 'uuml'],
|
|
||||||
[253, 'yacute'],
|
|
||||||
[254, 'thorn'],
|
|
||||||
[255, 'yuml'],
|
|
||||||
|
|
||||||
[8218, 'sbquo'],
|
|
||||||
[402, 'fnof'],
|
|
||||||
[8222, 'bdquo'],
|
|
||||||
[381, 'Zcaron'],
|
|
||||||
[382, 'zcaron'],
|
|
||||||
|
|
||||||
[128, 8364],
|
|
||||||
[130, 8218],
|
|
||||||
[131, 402],
|
|
||||||
[132, 8222],
|
|
||||||
[133, 8230],
|
|
||||||
[134, 8224],
|
|
||||||
[135, 8225],
|
|
||||||
[136, 710],
|
|
||||||
[137, 8240],
|
|
||||||
[138, 352],
|
|
||||||
[139, 8249],
|
|
||||||
[140, 338],
|
|
||||||
[142, 381],
|
|
||||||
[145, 8216],
|
|
||||||
[146, 8217],
|
|
||||||
[147, 8220],
|
|
||||||
[148, 8221],
|
|
||||||
[149, 8226],
|
|
||||||
[150, 8211],
|
|
||||||
[151, 8212],
|
|
||||||
[152, 732],
|
|
||||||
[153, 8482],
|
|
||||||
[154, 353],
|
|
||||||
[155, 8250],
|
|
||||||
[156, 339],
|
|
||||||
[158, 382],
|
|
||||||
[159, 376],
|
|
||||||
]
|
|
||||||
ENTITY_MAP = Hash.new do |h,k|
|
|
||||||
if k.kind_of?(Integer)
|
|
||||||
h[k] = Entity.new(k, nil)
|
|
||||||
else
|
|
||||||
raise Kramdown::Error, "Can't handle generic non-integer character reference '#{k}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ENTITY_TABLE.each do |code_point, data|
|
|
||||||
if data.kind_of?(String)
|
|
||||||
ENTITY_MAP[code_point] = ENTITY_MAP[data] = Entity.new(code_point, data)
|
|
||||||
else
|
|
||||||
raise "No entity object for code point #{data} found" unless ENTITY_MAP.has_key?(data)
|
|
||||||
ENTITY_MAP[code_point] = ENTITY_MAP[data]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the entity for the given +point_or_name+.
|
|
||||||
def entity(point_or_name)
|
|
||||||
ENTITY_MAP[point_or_name]
|
|
||||||
end
|
|
||||||
|
|
||||||
module_function :entity
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
module Utils
|
|
||||||
|
|
||||||
module HTML
|
|
||||||
|
|
||||||
# Convert the +entity+ to a string.
|
|
||||||
def entity_to_str(e)
|
|
||||||
if RUBY_VERSION >= '1.9' && (c = e.char.encode(@doc.parse_infos[:encoding]) rescue nil) && !ESCAPE_MAP.has_key?(c)
|
|
||||||
c
|
|
||||||
elsif @doc.options[:numeric_entities] || e.name.nil?
|
|
||||||
"&##{e.code_point};"
|
|
||||||
else
|
|
||||||
"&#{e.name};"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the string with the attributes of the element +el+.
|
|
||||||
def html_attributes(el)
|
|
||||||
(el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{escape_html(v.to_s, :no_entities)}\"" }.sort.join('')
|
|
||||||
end
|
|
||||||
|
|
||||||
ESCAPE_MAP = {
|
|
||||||
'<' => '<',
|
|
||||||
'>' => '>',
|
|
||||||
'&' => '&',
|
|
||||||
'"' => '"'
|
|
||||||
}
|
|
||||||
ESCAPE_ALL_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k})
|
|
||||||
ESCAPE_NO_ENTITIES_RE = Regexp.union(REXML::Parsers::BaseParser::REFERENCE_RE, ESCAPE_ALL_RE)
|
|
||||||
ESCAPE_NORMAL = Regexp.union(REXML::Parsers::BaseParser::REFERENCE_RE, /<|>|&/)
|
|
||||||
ESCAPE_RE_FROM_TYPE = {
|
|
||||||
:all => ESCAPE_ALL_RE,
|
|
||||||
:no_entities => ESCAPE_NO_ENTITIES_RE,
|
|
||||||
:text => ESCAPE_NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
# Escape the special HTML characters in the string +str+. The parameter +type+ specifies what
|
|
||||||
# is escaped: <tt>:all</tt> - all special HTML characters as well as entities,
|
|
||||||
# <tt>:no_entities</tt> - all special HTML characters but no entities, <tt>:text</tt> - all
|
|
||||||
# special HTML characters except the quotation mark but no entities.
|
|
||||||
def escape_html(str, type = :all)
|
|
||||||
str.gsub(ESCAPE_RE_FROM_TYPE[type]) {|m| ESCAPE_MAP[m] || m}
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
# Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#++
|
|
||||||
#
|
|
||||||
|
|
||||||
module Kramdown
|
|
||||||
|
|
||||||
# The kramdown version.
|
|
||||||
VERSION = '0.9.0'
|
|
||||||
|
|
||||||
end
|
|
@ -1,44 +0,0 @@
|
|||||||
body div#container {
|
|
||||||
margin : 0 auto;
|
|
||||||
max-width : 920px;
|
|
||||||
background-color : #f8f8f8;
|
|
||||||
padding : .7em;
|
|
||||||
font-size : 13.34px;
|
|
||||||
font-family : verdana, sans-serif;
|
|
||||||
border : 1px #E0E0E0 solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#container h2, body div#container h3, body div#content h4 {
|
|
||||||
padding-top : 10px;
|
|
||||||
border-top : 4px solid #E0E0E0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#container pre {
|
|
||||||
padding : 5px;
|
|
||||||
border-style : solid;
|
|
||||||
border-width : 1px;
|
|
||||||
border-color : #E0E0E0;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#container pre code {
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
border : none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div#container code {
|
|
||||||
font-family : courier, fixed;
|
|
||||||
display : inline-block;
|
|
||||||
padding : 0px 2px 0px 2px;
|
|
||||||
background-color : #F8F8FF;
|
|
||||||
border : 1px #E0E0E0 solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
body h4#title {
|
|
||||||
font-family : verdana, sans-serif;
|
|
||||||
display : block;
|
|
||||||
margin : 0 auto;
|
|
||||||
width : 920px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,487 +0,0 @@
|
|||||||
@media print {
|
|
||||||
/* print.css from blueprint CSS framework */
|
|
||||||
.page {
|
|
||||||
margin-left: 0.1in;
|
|
||||||
margin-right: 0.1in;
|
|
||||||
margin-top: 2in;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height:1.5;
|
|
||||||
font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;
|
|
||||||
font-family: Georgia, Times, serif;
|
|
||||||
color:#000;
|
|
||||||
background:none;
|
|
||||||
font-size:12pt;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
background:none;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
background:#ccc;
|
|
||||||
color:#ccc;
|
|
||||||
width:100%;
|
|
||||||
height:2px;
|
|
||||||
margin:2em 0;
|
|
||||||
padding:0;
|
|
||||||
border:none;
|
|
||||||
}
|
|
||||||
hr.space {
|
|
||||||
background:#fff;
|
|
||||||
color:#fff;
|
|
||||||
visibility:hidden;
|
|
||||||
}
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
font:.9em "Courier New", Monaco, Courier, monospace;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
line-height:0.8;
|
|
||||||
}
|
|
||||||
a img {
|
|
||||||
border:none;
|
|
||||||
}
|
|
||||||
p img.top {
|
|
||||||
margin-top:0;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
margin:1.5em;
|
|
||||||
padding:1em;
|
|
||||||
font-style:italic;
|
|
||||||
font-size:.9em;
|
|
||||||
}
|
|
||||||
.small {
|
|
||||||
font-size:.9em;
|
|
||||||
}
|
|
||||||
.large {
|
|
||||||
font-size:1.1em;
|
|
||||||
}
|
|
||||||
.quiet {
|
|
||||||
color:#999;
|
|
||||||
}
|
|
||||||
.hide {display:none;}
|
|
||||||
a:link, a:visited {
|
|
||||||
background:transparent;font-weight:700;text-decoration:underline;
|
|
||||||
}
|
|
||||||
a:link:after, a:visited:after {
|
|
||||||
content:" (" attr(href) ")";font-size:90%;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
page-break-inside: avoid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen {
|
|
||||||
/* screen.css from Safari Reader */
|
|
||||||
h1.title {
|
|
||||||
font-family: Palatino, Georgia, Times, "Times New Roman", serif;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.33em;
|
|
||||||
line-height: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.125em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.05em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: rgb(32, 0, 127);
|
|
||||||
}
|
|
||||||
|
|
||||||
.page a:visited {
|
|
||||||
color: rgb(32, 0, 127);
|
|
||||||
}
|
|
||||||
|
|
||||||
#article img {
|
|
||||||
/* Float images to the left, so that text will nicely flow around them. */
|
|
||||||
float: left;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#article img.reader-image-tiny {
|
|
||||||
/* Don't float very small images -- let them display where they occur in the text. */
|
|
||||||
float: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#article img.reader-image-large {
|
|
||||||
float: none;
|
|
||||||
margin: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float {
|
|
||||||
margin: 8px 0;
|
|
||||||
font-size: 65%;
|
|
||||||
line-height: 1.4;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float.left {
|
|
||||||
float: left;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float.right {
|
|
||||||
float: right;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float.full-width {
|
|
||||||
float: none;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
font: 20px Palatino, Georgia, Times, "Times New Roman", serif;
|
|
||||||
line-height: 160%;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page:first-of-type .title {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page table {
|
|
||||||
font-size: 0.9em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page.rtl table {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-number {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cached embed, .cached applet, .cached object {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#background {
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
-webkit-transform: translateZ(0);
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
margin-left: -431px;
|
|
||||||
left: 50%;
|
|
||||||
width: 862px;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#centered {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preloading #background {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preloading #centered {
|
|
||||||
-webkit-transform: translate3d(0, 100%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating #background {
|
|
||||||
-webkit-transition: opacity 0.40s ease-out;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating #fade-top {
|
|
||||||
-webkit-animation-name: fadeTopActivationFadeIn;
|
|
||||||
-webkit-animation-duration: 0.40s;
|
|
||||||
-webkit-animation-timing-function: ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating.skip-transition #fade-top {
|
|
||||||
-webkit-animation: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes fadeTopActivationFadeIn {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating.skip-transition #background {
|
|
||||||
-webkit-transition: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating #centered {
|
|
||||||
-webkit-transition: -webkit-transform 0.40s ease-out;
|
|
||||||
-webkit-transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activating.skip-transition #centered {
|
|
||||||
-webkit-transition: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deactivating #background {
|
|
||||||
-webkit-transition: opacity 0.40s ease-in;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deactivating #fade-top {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deactivating #fade-bottom {
|
|
||||||
-webkit-transition: opacity 0.40s ease-in;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deactivating #centered {
|
|
||||||
-webkit-transition: -webkit-transform 0.40s ease-in;
|
|
||||||
-webkit-transform: translate3d(0, 100%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.deactivating #hud {
|
|
||||||
-webkit-transition: opacity 0.25s ease-in;
|
|
||||||
opacity: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#drop-shadow {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 800px;
|
|
||||||
left: 10px;
|
|
||||||
border-width: 24px 24px;
|
|
||||||
-webkit-border-image: url(safari-resource:/ReaderDropShadow.png) 24 24 24 24 stretch stretch;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#hud {
|
|
||||||
position: fixed;
|
|
||||||
width: 314px;
|
|
||||||
height: 72px;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -157px;
|
|
||||||
bottom: 30px;
|
|
||||||
background: rgba(0, 0, 0, 0.75);
|
|
||||||
-webkit-border-radius: 12px;
|
|
||||||
z-index: 100;
|
|
||||||
opacity: 0;
|
|
||||||
-webkit-transition: opacity 0.75s;
|
|
||||||
pointer-events: auto;
|
|
||||||
zoom: reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud button {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
margin: 12px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud button:first-of-type {
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud button:last-of-type {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-zoom-out {
|
|
||||||
background: url(safari-resource:/ReaderHUDZoomOutInactive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-zoom-out:active {
|
|
||||||
background: url(safari-resource:/ReaderHUDZoomOutActive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-zoom-in {
|
|
||||||
background: url(safari-resource:/ReaderHUDZoomInInactive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-zoom-in:active {
|
|
||||||
background: url(safari-resource:/ReaderHUDZoomInActive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-mail {
|
|
||||||
background: url(safari-resource:/ReaderHUDMailContentsInactive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-mail:active {
|
|
||||||
background: url(safari-resource:/ReaderHUDMailContentsActive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-print {
|
|
||||||
background: url(safari-resource:/ReaderHUDPrintInactive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-print:active {
|
|
||||||
background: url(safari-resource:/ReaderHUDPrintActive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-exit {
|
|
||||||
background: url(safari-resource:/ReaderHUDCloseInactive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hud-exit:active {
|
|
||||||
background: url(safari-resource:/ReaderHUDCloseActive.png) no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#article {
|
|
||||||
/* The width of 819px here includes 19px for the WebKit scrollbar's width. */
|
|
||||||
/* The padding-right of 8px separates the scrollbar from the article itself. */
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
left: 34px;
|
|
||||||
width: 819px;
|
|
||||||
padding-right: 8px;
|
|
||||||
overflow: scroll;
|
|
||||||
z-index: 0;
|
|
||||||
outline: none;
|
|
||||||
pointer-events: auto;
|
|
||||||
-webkit-user-select: auto;
|
|
||||||
-webkit-transform: translateZ(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-fade {
|
|
||||||
position: absolute;
|
|
||||||
left: 34px;
|
|
||||||
height: 36px;
|
|
||||||
width: 800px;
|
|
||||||
z-index: 10;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fade-top {
|
|
||||||
top: 0;
|
|
||||||
background: url(safari-resource:/ReaderFadeTop.png) repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fade-bottom {
|
|
||||||
bottom: 0;
|
|
||||||
background: url(safari-resource:/ReaderFadeBottom.png) repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#resize-indicator {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
background: url(safari-resource:/TopSitesCornerResize.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
.page:only-of-type .page-number {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-number {
|
|
||||||
display: block;
|
|
||||||
font: bold 11px Helvetica, sans-serif;
|
|
||||||
margin-left: 12px;
|
|
||||||
color: #B2B2B2;
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 10px;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page:first-of-type {
|
|
||||||
margin-top: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page:last-of-type {
|
|
||||||
margin-bottom: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
width: 658px;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 45px 70px;
|
|
||||||
color: black;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #c3c3c3;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
-webkit-transition: height .5s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page.rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
|
|
||||||
#incoming-page-placeholder {
|
|
||||||
height: 30px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#incoming-page-corner {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#incoming-page-spinner {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
float: right;
|
|
||||||
background: url(safari-resource:/ReaderSpinner.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
#incoming-page-text {
|
|
||||||
float: right;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-left: 8px;
|
|
||||||
color: #B2B2B2;
|
|
||||||
font: bold 11px Helvetica, sans-serif;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#next-page-container {
|
|
||||||
position:absolute;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-transition {
|
|
||||||
-webkit-transition: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
body {
|
|
||||||
font-size:13px;
|
|
||||||
font-family:verdana, sans-serif;
|
|
||||||
line-height:1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 { font-size:1.6em; }
|
|
||||||
h2 { font-size:1.5em; }
|
|
||||||
h3 { font-size:1.4em; font-weight:300; }
|
|
||||||
h4 { font-size:1em; }
|
|
||||||
|
|
||||||
h1, h2, h3, h4 {
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
||||||
margin-top:20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
padding-top:5px;
|
|
||||||
border-top:1px solid #AAA;
|
|
||||||
margin-top:40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin:15px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding:5px;
|
|
||||||
border:1px solid #EEEEEE;
|
|
||||||
background-color:#F6F6F6;
|
|
||||||
margin:0 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
padding:5px;
|
|
||||||
background-color:#F6F6F6;
|
|
||||||
border:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family:courier, fixed;
|
|
||||||
display:inline-block;
|
|
||||||
padding:0px 2px 0px 2px;
|
|
||||||
background-color: #F6F6F6;
|
|
||||||
border:1px #E0E0E0 solid;
|
|
||||||
}
|
|
||||||
ul li {
|
|
||||||
margin:2px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
|||||||
|
|
||||||
if !exists('g:VMPoutputformat')
|
|
||||||
let g:VMPoutputformat = 'html'
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:VMPoutputdirectory')
|
|
||||||
let g:VMPoutputdirectory = '/tmp'
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:VMPhtmlreader')
|
|
||||||
if has('mac')
|
|
||||||
let g:VMPhtmlreader = 'open'
|
|
||||||
elseif has('win32') || has('win64')
|
|
||||||
let g:VMPhtmlreader = 'start'
|
|
||||||
elseif has('unix') && executable('xdg-open')
|
|
||||||
let g:VMPhtmlreader = 'xdg-open'
|
|
||||||
else
|
|
||||||
let g:VMPhtmlreader = ''
|
|
||||||
end
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:VMPstylesheet')
|
|
||||||
let g:VMPstylesheet = 'github.css'
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
function! PreviewMKD()
|
|
||||||
|
|
||||||
ruby << RUBY
|
|
||||||
|
|
||||||
runtime = Vim.evaluate('&runtimepath').split(',')
|
|
||||||
runtime.each { |path| $LOAD_PATH.unshift(File.join(path, 'plugin', 'vim-markdown-preview')) }
|
|
||||||
|
|
||||||
css_base = runtime.detect { |path| File.exists? File.join(path, 'plugin', 'vmp.vim') }
|
|
||||||
stylesheet = File.join(css_base, 'plugin', 'vim-markdown-preview', 'stylesheets',
|
|
||||||
Vim.evaluate('g:VMPstylesheet'))
|
|
||||||
name = Vim::Buffer.current.name.nil? ? 'Untitled' : File.basename(Vim::Buffer.current.name)
|
|
||||||
output_dir = Vim.evaluate('g:VMPoutputdirectory')
|
|
||||||
|
|
||||||
|
|
||||||
contents = Array.new(VIM::Buffer.current.count) { |i| VIM::Buffer.current[i + 1] }.join("\n")
|
|
||||||
|
|
||||||
require('kramdown/kramdown')
|
|
||||||
|
|
||||||
layout = <<-LAYOUT
|
|
||||||
<!DOCTYPE html
|
|
||||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
|
|
||||||
<link rel="stylesheet"
|
|
||||||
href="#{stylesheet}">
|
|
||||||
</link>
|
|
||||||
|
|
||||||
<title> #{name} </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="container">
|
|
||||||
<div id="centered">
|
|
||||||
<div id="article">
|
|
||||||
<div class="page">
|
|
||||||
#{Kramdown::Document.new(contents).to_html}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
LAYOUT
|
|
||||||
|
|
||||||
case Vim.evaluate('g:VMPoutputformat')
|
|
||||||
when 'html'
|
|
||||||
reader = Vim.evaluate('g:VMPhtmlreader')
|
|
||||||
|
|
||||||
if reader == ''
|
|
||||||
Vim.message('No suitable HTML reader found! Please set g:VMPhtmlreader.')
|
|
||||||
else
|
|
||||||
file = File.join(output_dir, name + '.html')
|
|
||||||
File.open(file, 'w') { |f| f.write(layout) }
|
|
||||||
Vim.command("silent ! #{reader} '%s'" % [ file ])
|
|
||||||
Vim.command 'redraw!'
|
|
||||||
end
|
|
||||||
when 'pdf'
|
|
||||||
Vim.message('output format not implemented yet.')
|
|
||||||
else
|
|
||||||
Vim.message('Unrecongized output format! Check g:VMPoutputformat.')
|
|
||||||
end
|
|
||||||
|
|
||||||
RUBY
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
:command! Mm :call PreviewMKD()
|
|
Loading…
Reference in New Issue
Block a user