lib/trac-wiki/parser.rb in trac-wiki-0.1.9 vs lib/trac-wiki/parser.rb in trac-wiki-0.1.12
- old
+ new
@@ -1,7 +1,8 @@
require 'cgi'
require 'uri'
+require 'iconv'
# :main: TracWiki
# The TracWiki parses and translates Trac formatted text into
# XHTML. Creole is a lightweight markup syntax similar to what many
@@ -41,11 +42,13 @@
# Allowed url schemes
# Examples: http https ftp ftps
attr_accessor :allowed_schemes
+ attr_accessor :headings
+
# Disable url escaping for local links
# Escaping: [[/Test]] --> %2FTest
# No escaping: [[/Test]] --> Test
attr_writer :no_escape
def no_escape?; @no_escape; end
@@ -56,16 +59,34 @@
def no_link?; @no_link; end
attr_writer :math
def math?; @math; end
+ attr_writer :edit_heading
+ def edit_heading?; @edit_heading; end
+
+ # understand merge tags (see diff3(1))
+ # >>>>>>> mine
+ # ||||||| orig
+ # =======
+ # <<<<<<< yours
+ # convert to <div class="merge merge-mine">mine</div>
attr_writer :merge
def merge?; @merge; end
+ # every heading will had id, generated from heading text
+ attr_writer :id_from_heading
+ def id_from_heading?; @id_from_heading; end
+
+ # when id_from_heading, non ascii char are transliterated to ascii
+ attr_writer :id_translit
+ def id_translit?; @id_translit; end
+
# Create a new Parser instance.
def initialize(text, options = {})
@allowed_schemes = %w(http https ftp ftps)
+ @anames = {}
@text = text
@no_escape = nil
options.each_pair {|k,v| send("#{k}=", v) }
end
@@ -81,26 +102,39 @@
# parser = Parser.new("**Hello //World//**")
# parser.to_html
# #=> "<p><strong>Hello <em>World</em></strong></p>"
def to_html
@out = ''
+ @edit_heading_class = 'editheading'
+ @headings = [ {level: 0, sline: 1 } ]
@p = false
@stack = []
@stacki = []
@was_math = false
+ @line_no = 1
parse_block(@text)
@out
end
+ def make_toc_html
+ @out = ''
+ parse_block(make_toc)
+ end
+
protected
# Escape any characters with special meaning in HTML using HTML
- # entities.
+ # entities. (&<>" not ')
def escape_html(string)
- CGI::escapeHTML(string)
+ #CGI::escapeHTML(string)
+ Parser.escapeHTML(string)
end
+ def self.escapeHTML(string)
+ string.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<')
+ end
+
# Escape any characters with special meaning in URLs using URL
# encoding.
def escape_url(string)
CGI::escape(string)
end
@@ -174,11 +208,14 @@
# Example custom behaviour:
#
# make_local_link("LocalLink") #=> "/LocalLink"
# make_local_link("Wikipedia:Bread") #=> "http://en.wikipedia.org/wiki/Bread"
def make_local_link(link) #:doc:
- no_escape? ? link : escape_url(link)
+ return link if no_escape?
+ link, anch = link.split(/#/, 2)
+ return escape_url(link) if ! anch
+ "#{escape_url(link)}##{escape_url(anch)}"
end
# Sanatize a direct url (e.g. http://wikipedia.org/). The default
# behaviour returns the original link as-is.
#
@@ -244,26 +281,49 @@
return '' if a.empty?
return ' ' + a.map{|k,v| "#{k}=\"#{v}\"" }.sort.join(' ')
end
def make_headline(level, text, aname)
- ret = "<h#{level}>" << escape_html(text) << "</h#{level}>"
+ ret = "<h#{level}"
if aname
- ret = "<a name=\"#{ escape_html(aname) }\"/>" + ret
+ ret += " id=\"#{ escape_html(aname) }\""
end
+ ret += ">" + escape_html(text)
+
+ if edit_heading?
+ ret += edit_heading_link(@headings.size - 1)
+ end
+
+ ret += "</h#{level}>"
ret
end
+ def edit_heading_link(section)
+ "<a class='#{@edit_heading_class}' href=\"?edit=#{section}\">edit</a>"
+ end
+
def make_explicit_link(link)
begin
uri = URI.parse(link)
return uri.to_s if uri.scheme && @allowed_schemes.include?(uri.scheme)
rescue URI::InvalidURIError
end
make_local_link(link)
end
+
+ def make_toc
+ @headings.map do |h|
+ if h[:level] < 1
+ ''
+ else
+ ind = " " * (h[:level] - 1)
+ "#{ind}* [[##{h[:aname]}|#{h[:title]}]]\n"
+ end
+ end.join
+ end
+
def parse_inline(str)
until str.empty?
case str
# raw url
when /\A(!)?((https?|ftps?):\/\/\S+?)(?=([\]\,.?!:;"'\)]+)?(\s|$))/
@@ -510,14 +570,16 @@
end_paragraph
@out << '<hr/>'
# heading == Wiki Ruless ==
# heading == Wiki Ruless == #tag
- when str =~ /\A\s*(={1,6})\s*(.*?)\s*=*\s*(#(\S*))?\s*$(\r?\n)?/
+ when str =~ /\A[[:blank:]]*(={1,6})\s*(.*?)\s*=*\s*(#(\S*))?\s*$(\r?\n)?/
level = $1.size
title= $2
- aname= $4
+ aname= aname_nice($4, title)
+ @headings.last[:eline] = @line_no - 1
+ @headings.push({ :title => title, :sline => @line_no, :aname => aname, :level => level, })
end_paragraph
@out << make_headline(level, title, aname)
# table row
when str =~ /\A[ \t]*\|\|(.*)$(\r?\n)?/
@@ -574,12 +636,36 @@
parse_inline(text)
end
else # case str
raise "Parse error at #{str[0,30].inspect}"
end
+ @line_no += ($`+$&).count("\n")
str = $'
end
end_paragraph
+ @headings.last[:eline] = @line_no - 1
@out
end
+
+ def aname_nice(aname, title)
+
+ if aname.nil? && id_from_heading?
+ aname = title.gsub /\s+/, '_'
+ if id_translit?
+ aname = Iconv.iconv('ascii//translit', 'utf-8', aname).join
+ end
+ end
+ return nil if aname.nil?
+ aname_ori = aname
+ count = 2
+ while @anames[aname]
+ aname = aname_ori + ".#{count}"
+ count+=1
+ end
+ @anames[aname] = true
+ aname
+ end
+
+
+
end
end