wikicloth 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
- require 'rubygems'
2
- require 'builder'
3
1
  require 'rexml/document'
4
2
 
5
3
  module WikiCloth
@@ -11,10 +9,10 @@ class WikiBuffer::HTMLElement < WikiBuffer
11
9
  'h1','h2','h3','h4','h5','h6','p','table','tr','td','th','tbody','thead','tfoot']
12
10
  ALLOWED_ATTRIBUTES = ['src','id','name','style','class','href','start','value','colspan','align','border',
13
11
  'cellpadding','cellspacing','name','valign','color','rowspan','nowrap','title','rel','for']
14
- ESCAPED_TAGS = [ 'nowiki', 'pre', 'code' ]
12
+ ESCAPED_TAGS = [ 'nowiki','pre','code' ]
15
13
  SHORT_TAGS = [ 'meta','br','hr']
16
14
  NO_NEED_TO_CLOSE = ['li','p'] + SHORT_TAGS
17
- DISABLE_GLOBALS_FOR = ESCAPED_TAGS + [ 'math' ]
15
+ DISABLE_GLOBALS_FOR = ESCAPED_TAGS
18
16
 
19
17
  def initialize(d="",options={},check=nil)
20
18
  super("",options)
@@ -38,78 +36,41 @@ class WikiBuffer::HTMLElement < WikiBuffer
38
36
  end
39
37
  end
40
38
 
39
+ def skip_html?
40
+ return Extension.skip_html?(self.element_name) if Extension.element_exists?(self.element_name)
41
+ DISABLE_GLOBALS_FOR.include?(self.element_name) ? true : false
42
+ end
43
+
41
44
  def run_globals?
45
+ return false if self.in_template? && self.element_name == "noinclude"
46
+ return false if !self.in_template? && self.element_name == "includeonly"
47
+ return Extension.run_globals?(self.element_name) if Extension.element_exists?(self.element_name)
42
48
  return DISABLE_GLOBALS_FOR.include?(self.element_name) ? false : true
43
49
  end
44
50
 
45
- def to_s
51
+ def to_html
46
52
  if NO_NEED_TO_CLOSE.include?(self.element_name)
47
53
  return "<#{self.element_name} />" if SHORT_TAGS.include?(self.element_name)
48
54
  return "</#{self.element_name}><#{self.element_name}>" if @tag_check == self.element_name
49
55
  end
50
56
 
51
57
  if ESCAPED_TAGS.include?(self.element_name)
58
+ # remove empty first line
59
+ self.element_content = $1 if self.element_content =~ /^\s*\n(.*)$/m
52
60
  # escape all html inside this element
53
61
  self.element_content = self.element_content.gsub('<','&lt;').gsub('>','&gt;')
54
62
  # hack to fix <code><nowiki> nested mess
55
63
  self.element_content = self.element_content.gsub(/&lt;[\/]*\s*nowiki\s*&gt;/,'')
56
64
  end
57
65
 
58
- lhandler = @options[:link_handler]
59
66
  case self.element_name
60
- when "math"
61
- blahtex_path = @options[:blahtex_path] || '/usr/bin/blahtex'
62
- blahtex_png_path = @options[:blahtex_png_path] || '/tmp'
63
-
64
- if File.exists?(blahtex_path)
65
- begin
66
- response = `echo '#{self.element_content}' | #{blahtex_path} --png --mathml --png-directory #{blahtex_png_path}`
67
- xml_response = REXML::Document.new(response).root
68
- if @options[:blahtex_html_prefix]
69
- file_md5 = xml_response.elements["png/md5"].text
70
- return "<img src=\"#{File.join(@options[:blahtex_html_prefix],"#{file_md5}.png")}\" />"
71
- else
72
- html = xml_response.elements["mathml/markup"].text
73
- eattr = { "xmlns" => "http://www.w3.org/1998/Math/MathML" }.merge(self.element_attributes)
74
- return elem.tag!(self.element_name, eattr) { |x| x << xml_response.elements["mathml/markup"].children.to_s }
75
- end
76
- rescue => err
77
- return "<span class=\"error\">Unable to parse MathML: #{err}</span>"
78
- end
79
- else
80
- return "<span class=\"error\">blatex binary not found</span>"
81
- end
82
67
  when "template"
68
+ @options[:link_handler].cache({ :name => self.element_attributes["__name"], :content => self.element_content, :md5 => self.element_attributes["__hash"] })
83
69
  return self.element_content
84
70
  when "noinclude"
85
71
  return self.in_template? ? "" : self.element_content
86
72
  when "includeonly"
87
73
  return self.in_template? ? self.element_content : ""
88
- when "ref"
89
- self.element_name = "sup"
90
- named_ref = self.name_attribute
91
- ref = lhandler.find_reference_by_name(named_ref) unless named_ref.nil?
92
- if ref.nil?
93
- lhandler.references << { :name => named_ref, :value => self.element_content, :count => 0 }
94
- ref = lhandler.references.last
95
- end
96
- ref_id = (named_ref.nil? ? "" : "#{named_ref}_") + "#{lhandler.reference_index(ref)}-#{ref[:count]}"
97
- self.params << { :name => "id", :value => "cite_ref-#{ref_id}" }
98
- self.params << { :name => "class", :value => "reference" }
99
- self.element_content = "[<a href=\"#cite_note-" + (named_ref.nil? ? "" : "#{named_ref}_") +
100
- "#{lhandler.reference_index(ref)}\">#{lhandler.reference_index(ref)}</a>]"
101
- ref[:count] += 1
102
- when "references"
103
- ref_count = 0
104
- self.element_name = "ol"
105
- self.element_content = lhandler.references.collect { |r|
106
- ref_count += 1
107
- ref_name = (r[:name].nil? ? "" : r[:name].to_slug + "_")
108
- ret = "<li id=\"cite_note-#{ref_name}#{ref_count}\"><b>"
109
- 1.upto(r[:count]) { |x| ret += "<a href=\"#cite_ref-#{ref_name}#{ref_count}-#{x-1}\">" +
110
- (r[:count] == 1 ? "^" : (x-1).to_s(26).tr('0-9a-p', 'a-z')) + "</a> " }
111
- ret += "</b> #{r[:value]}</li>"
112
- }.to_s
113
74
  when "nowiki"
114
75
  return self.element_content
115
76
  when "a"
@@ -119,6 +80,10 @@ class WikiBuffer::HTMLElement < WikiBuffer
119
80
  # if a element has no href attribute, or href starts with / or ?
120
81
  return elem.tag!(self.element_name, self.element_attributes) { |x| x << self.element_content }
121
82
  end
83
+ else
84
+ if Extension.element_exists?(self.element_name)
85
+ return Extension.html_elements[self.element_name][:klass].new(@options).instance_exec( self, &Extension.html_elements[self.element_name][:block] ).to_s
86
+ end
122
87
  end
123
88
 
124
89
  tmp = elem.tag!(self.element_name, self.element_attributes) { |x| x << self.element_content }
@@ -129,9 +94,9 @@ class WikiBuffer::HTMLElement < WikiBuffer
129
94
  tmp
130
95
  end
131
96
 
132
- def name_attribute
133
- params.each { |p| return p[:value].to_slug if p.kind_of?(Hash) && p[:name] == "name" }
134
- return nil
97
+ def get_attribute_by_name(name)
98
+ params.each { |p| return p[:value] if p.kind_of?(Hash) && p[:name] == name }
99
+ nil
135
100
  end
136
101
 
137
102
  def element_attributes
@@ -267,7 +232,7 @@ class WikiBuffer::HTMLElement < WikiBuffer
267
232
  self.data = "</#{self.data}>"
268
233
  return false
269
234
  else
270
- self.element_content += "&lt;/#{self.data}&gt;"
235
+ self.element_content += skip_html? ? "</#{self.data}>" : "&lt;/#{self.data}&gt;"
271
236
  @start_tag = 0
272
237
  self.data = ""
273
238
  end
@@ -275,9 +240,9 @@ class WikiBuffer::HTMLElement < WikiBuffer
275
240
 
276
241
  else
277
242
  if @start_tag == 0 && ESCAPED_TAGS.include?(self.element_name)
278
- self.data += self.escape_char(current_char)
243
+ self.data << self.escape_char(current_char)
279
244
  else
280
- self.data += current_char
245
+ self.data << current_char
281
246
  end
282
247
  end
283
248
  return true
@@ -12,7 +12,7 @@ class WikiBuffer::Link < WikiBuffer
12
12
  @internal_link ||= false
13
13
  end
14
14
 
15
- def to_s
15
+ def to_html
16
16
  link_handler = @options[:link_handler]
17
17
  unless self.internal_link || params[0].strip !~ /^\s*(([a-z]+):\/\/|[\?\/])/
18
18
  return link_handler.external_link("#{params[0]}".strip, "#{params[1]}".strip)
@@ -22,9 +22,10 @@ class WikiBuffer::Link < WikiBuffer
22
22
  return "[#{params[0]}]"
23
23
  when params[0] =~ /^:(.*)/
24
24
  return link_handler.link_for(params[0],params[1])
25
- when params[0] =~ /^\s*([a-zA-Z0-9-]+)\s*:(.*)$/
25
+ when params[0] =~ /^\s*([^\]\s:]+)\s*:(.*)$/
26
26
  return link_handler.link_for_resource($1,$2,params[1..-1])
27
27
  else
28
+ return "" if params[0].blank? && params[1].blank?
28
29
  return link_handler.link_for(params[0],params[1])
29
30
  end
30
31
  end
@@ -24,7 +24,7 @@ class WikiBuffer::Table < WikiBuffer
24
24
  @rows ||= [ [] ]
25
25
  end
26
26
 
27
- def to_s
27
+ def to_html
28
28
  row_count = 0
29
29
  ret = "<table" + (params[0].blank? ? "" : " #{params[0].strip}") + ">"
30
30
  ret += "<caption" + (self.table_caption_attributes.blank? ? "" : " #{table_caption_attributes.strip}") +
@@ -180,7 +180,7 @@ class WikiBuffer::Table < WikiBuffer
180
180
  @start_row = true
181
181
 
182
182
  else
183
- self.data += current_char
183
+ self.data << current_char
184
184
  end
185
185
 
186
186
  return true
@@ -1,4 +1,5 @@
1
1
  require 'expression_parser'
2
+ require 'digest/md5'
2
3
  require 'uri'
3
4
 
4
5
  module WikiCloth
@@ -24,7 +25,7 @@ class WikiBuffer::Var < WikiBuffer
24
25
  end
25
26
 
26
27
  def skip_links?
27
- true
28
+ false
28
29
  end
29
30
 
30
31
  def skip_html?
@@ -40,18 +41,24 @@ class WikiBuffer::Var < WikiBuffer
40
41
  end
41
42
 
42
43
  def function_name
43
- @fname
44
+ @fname.nil? ? nil : @fname.strip
44
45
  end
45
46
 
46
- def to_s
47
+ def to_html
48
+ return "" if will_not_be_rendered
49
+
47
50
  if self.is_function?
51
+ if Extension.function_exists?(function_name)
52
+ return Extension.functions[function_name][:klass].new(@options).instance_exec( params.collect { |p| p.strip }, &Extension.functions[function_name][:block] ).to_s
53
+ end
48
54
  ret = default_functions(function_name,params.collect { |p| p.strip })
49
55
  ret ||= @options[:link_handler].function(function_name, params.collect { |p| p.strip })
50
56
  ret.to_s
51
57
  elsif self.is_param?
52
58
  ret = nil
53
59
  @options[:buffer].buffers.reverse.each do |b|
54
- ret = b.get_param(params[0].downcase,params[1]) if b.instance_of?(WikiBuffer::HTMLElement) && b.element_name == "template"
60
+ ret = b.get_param(params[0],params[1]) if b.instance_of?(WikiBuffer::HTMLElement) && b.element_name == "template"
61
+ break unless ret.nil?
55
62
  end
56
63
  ret.to_s
57
64
  else
@@ -60,25 +67,46 @@ class WikiBuffer::Var < WikiBuffer
60
67
  b.element_name == "template" }.compact
61
68
  if template_stack.last == params[0]
62
69
  debug_tree = @options[:buffer].buffers.collect { |b| b.debug }.join("-->")
63
- "<span class=\"error\">Template loop detected: &#123;&#123;#{debug_tree}&#125;&#125;</span>"
70
+ "<span class=\"error\">#{I18n.t('template loop detected', :tree => debug_tree)}</span>"
64
71
  else
65
- ret = @options[:link_handler].include_resource("#{params[0]}".strip,params[1..-1]).to_s
72
+ key = params[0].to_s.strip
73
+ key_options = params[1..-1].collect { |p| p.is_a?(Hash) ? { :name => p[:name].strip, :value => p[:value].strip } : p.strip }
74
+ key_options ||= []
75
+ key_digest = Digest::MD5.hexdigest(key_options.to_a.sort {|x,y| (x.is_a?(Hash) ? x[:name] : x) <=> (y.is_a?(Hash) ? y[:name] : y) }.inspect)
76
+
77
+ return @options[:params][key] if @options[:params].has_key?(key)
78
+ # if we have a valid cache fragment use it
79
+ return @options[:cache][key][key_digest] unless @options[:cache].nil? || @options[:cache][key].nil? || @options[:cache][key][key_digest].nil?
80
+
81
+ ret = @options[:link_handler].include_resource(key,key_options).to_s
82
+
66
83
  ret.gsub!(/<!--(.|\s)*?-->/,"")
67
84
  count = 0
68
- tag_attr = self.params[1..-1].collect { |p|
85
+ tag_attr = key_options.collect { |p|
69
86
  if p.instance_of?(Hash)
70
- "#{p[:name].downcase}=\"#{p[:value]}\""
87
+ "#{p[:name]}=\"#{p[:value].gsub(/"/,'&quot;')}\""
71
88
  else
72
89
  count += 1
73
- "#{count}=\"#{p}\""
90
+ "#{count}=\"#{p.gsub(/"/,'&quot;')}\""
74
91
  end
75
92
  }.join(" ")
76
- self.data = ret.blank? ? "" : "<template __name=\"#{params[0]}\" #{tag_attr}>#{ret}</template>"
93
+
94
+ self.data = ret.blank? ? "" : "<template __name=\"#{key}\" __hash=\"#{key_digest}\" #{tag_attr}>#{ret}</template>"
77
95
  ""
78
96
  end
79
97
  end
80
98
  end
81
99
 
100
+ def will_not_be_rendered
101
+ @options[:buffer].buffers.reverse.each do |buffer|
102
+ if buffer.instance_of?(WikiBuffer::Var) && buffer.is_function?
103
+ return true if buffer.function_name == "#if" && buffer.params.size == 2 && buffer.params[0].strip.blank?
104
+ return true if buffer.function_name == "#if" && buffer.params.size == 3 && !buffer.params[0].strip.blank?
105
+ end
106
+ end
107
+ false
108
+ end
109
+
82
110
  def default_functions(name,params)
83
111
  case name
84
112
  when "#if"
@@ -88,12 +116,12 @@ class WikiBuffer::Var < WikiBuffer
88
116
  default = nil
89
117
  for p in params[1..-1]
90
118
  temp = p.split("=")
91
- if temp.length == 1 && p == params.last
119
+ if p !~ /=/ && temp.length == 1 && p == params.last
92
120
  return p
93
- elsif temp.instance_of?(Array) && temp.length > 1
121
+ elsif temp.instance_of?(Array) && temp.length > 0
94
122
  test = temp.first.strip
95
- default = temp[1].strip if test == "#default"
96
- return temp[1].strip if test == match
123
+ default = temp[1..-1].join("=").strip if test == "#default"
124
+ return temp[1..-1].join("=").strip if test == match || (test == "none" && match.blank?)
97
125
  end
98
126
  end
99
127
  default.nil? ? "" : default
@@ -101,7 +129,7 @@ class WikiBuffer::Var < WikiBuffer
101
129
  begin
102
130
  ExpressionParser::Parser.new.parse(params.first)
103
131
  rescue RuntimeError
104
- "Expression error: #{$!}"
132
+ I18n.t('expression error', :error => $!)
105
133
  end
106
134
  when "#ifexpr"
107
135
  val = false
@@ -147,20 +175,40 @@ class WikiBuffer::Var < WikiBuffer
147
175
  when "ucfirst"
148
176
  params.first.capitalize
149
177
  when "lcfirst"
150
- params.first[0,1].downcase + params.first[1,-1]
178
+ params.first[0,1].downcase + params.first[1..-1]
179
+ when "anchorencode"
180
+ params.first.gsub(/\s+/,'_')
151
181
  when "plural"
152
- params.first.to_i > 1 ? params[1] : params[2]
182
+ begin
183
+ expr_value = ExpressionParser::Parser.new.parse(params.first)
184
+ expr_value.to_i == 1 ? params[1] : params[2]
185
+ rescue RuntimeError
186
+ I18n.t('expression error', :error => $!)
187
+ end
153
188
  when "ns"
154
- values = { "" => "", "0" => "", "1" => "Talk", "talk" => "Talk", "2" => "User", "user" => "User",
155
- "3" => "User talk", "user_talk" => "User talk", "4" => "Meta", "project" => "Meta",
156
- "5" => "Meta talk", "project_talk" => "Meta talk", "6" => "File", "image" => "File",
157
- "7" => "File talk", "image_talk" => "File talk", "8" => "MediaWiki", "mediawiki" => "MediaWiki",
158
- "9" => "MediaWiki talk", "mediawiki_talk" => "MediaWiki talk", "10" => "Template",
159
- "template" => "Template", "11" => "Template talk", "template_talk" => "Template talk",
160
- "12" => "Help", "help" => "Help", "13" => "Help talk", "help_talk" => "Help talk",
161
- "14" => "Category", "category" => "Category", "15" => "Category talk", "category_talk" => "Category talk",
162
- "-2" => "Media", "media" => "Media", "-1" => "Special", "special" => "Special" }
163
- values[params.first.gsub(/\s+/,'_').downcase]
189
+ values = {
190
+ "" => "", "0" => "",
191
+ "1" => localise_ns("Talk"), "talk" => localise_ns("Talk"),
192
+ "6" => localise_ns("File"), "file" => localise_ns("File"), "image" => localise_ns("File"),
193
+ "10" => localise_ns("Template"), "template" => localise_ns("Template"),
194
+ "14" => localise_ns("Category"), "category" => localise_ns("Category"),
195
+ "-1" => localise_ns("Special"), "special" => localise_ns("Special"),
196
+ "12" => localise_ns("Help"), "help" => localise_ns("Help"),
197
+ "-2" => localise_ns("Media"), "media" => localise_ns("Media") }
198
+
199
+ values[localise_ns(params.first,:en).gsub(/\s+/,'_').downcase]
200
+ when "#language"
201
+ WikiNamespaces.language_name(params.first)
202
+ when "#tag"
203
+ return "" if params.empty?
204
+ elem = Builder::XmlMarkup.new
205
+ return elem.tag!(params.first) if params.length == 1
206
+ return elem.tag!(params.first) { |e| e << params.last } if params.length == 2
207
+ tag_attrs = {}
208
+ params[1..-2].each do |attr|
209
+ tag_attrs[$1] = $2 if attr =~ /^\s*([\w]+)\s*=\s*"(.*)"\s*$/
210
+ end
211
+ elem.tag!(params.first,tag_attrs) { |e| e << params.last }
164
212
  when "debug"
165
213
  ret = nil
166
214
  case params.first
@@ -183,6 +231,10 @@ class WikiBuffer::Var < WikiBuffer
183
231
  end
184
232
  end
185
233
 
234
+ def localise_ns(name,lang=nil)
235
+ WikiNamespaces.localise_ns(name,lang)
236
+ end
237
+
186
238
  def is_param?
187
239
  @tag_size == 3 ? true : false
188
240
  end
@@ -227,7 +279,7 @@ class WikiBuffer::Var < WikiBuffer
227
279
  end
228
280
 
229
281
  else
230
- self.data += current_char
282
+ self.data << current_char
231
283
  if @tag_start
232
284
  # FIXME: template params and templates colliding
233
285
  if @tag_size > 3
@@ -1,19 +1,12 @@
1
- require 'rubygems'
2
- require 'builder'
3
-
1
+ # encoding: utf-8
4
2
  module WikiCloth
5
3
 
6
- class WikiLinkHandler
4
+ class WikiLinkHandler < WikiNamespaces
7
5
 
8
- FILE_NAMESPACES = ["datei","image","file","media"]
9
- CATEGORY_NAMESPACES = ['kategorie','category']
10
- LANGUAGE_NAMESPACES = ['af','am','ang','ar','arc','ast','az','bn','zh-min-nan','ba','be','be-x-old','bar','bs','br','bg','ca',
11
- 'ceb','cs','co','cy','da','de','dv','et','el','es','eo','eu','fa','fo','fr','fy','ga','gd','gl','gan','ko','hy','hi','hr',
12
- 'io','id','is','it','he','jv','kn','pam','ka','sw','ku','la','lv','lb','lt','hu','mk','mg','ml','mr','arz','ms','nah','nl',
13
- 'ja','no','nn','oc','uz','pap','nds','pl','pt','ksh','ro','qu','ru','sa','sco','sq','scn','simple','sk','sl','sr','sh',
14
- 'fi','sv','tl','ta','th','tg','tr','uk','ur','vi','zh-classical','yi','bat-smg','zh','lo','en','gn','map-bms','pdc','eml',
15
- 'ki','hak','ia','ky','lad','nds-nl','ne','nrm','nov','sm','si','su','kab','te','vec','fiu-vro','wa','war','wuu','zh-yue',
16
- 'diq']
6
+ FILE_NAMESPACES = file_namespace_names
7
+ MEDIA_NAMESPACES = media_namespace_names
8
+ CATEGORY_NAMESPACES = category_namespace_names
9
+ LANGUAGE_NAMESPACES = language_namespace_names
17
10
 
18
11
  def references
19
12
  @references ||= []
@@ -31,6 +24,10 @@ class WikiLinkHandler
31
24
  nil
32
25
  end
33
26
 
27
+ def cache(item)
28
+ nil
29
+ end
30
+
34
31
  def section_list(root=nil)
35
32
  ret = []
36
33
  root = sections[0].children if root.nil?
@@ -44,7 +41,7 @@ class WikiLinkHandler
44
41
  end
45
42
 
46
43
  def toc(sections, toc_numbered=false)
47
- ret = "<table id=\"toc\" class=\"toc\" summary=\"Contents\"><tr><td><div style=\"font-weight:bold\">Table of Contents</div><ul>"
44
+ ret = "<table id=\"toc\" class=\"toc\" summary=\"Contents\"><tr><td><div style=\"font-weight:bold\">#{I18n.t('table of contents')}</div><ul>"
48
45
  previous_depth = 1
49
46
  indices = []
50
47
  section_list(sections).each do |section|
@@ -146,23 +143,19 @@ class WikiLinkHandler
146
143
  end
147
144
 
148
145
  def include_resource(resource, options=[])
149
- if self.params.has_key?(resource)
150
- self.params[resource]
146
+ @template_cache ||= {}
147
+ if @template_cache[resource]
148
+ @included_templates[resource] += 1
149
+ @template_cache[resource]
151
150
  else
152
- @template_cache ||= {}
153
- if @template_cache[resource]
151
+ ret = template(resource)
152
+ unless ret.nil?
153
+ @included_templates ||= {}
154
+ @included_templates[resource] ||= 0
154
155
  @included_templates[resource] += 1
155
- @template_cache[resource]
156
- else
157
- ret = template(resource)
158
- unless ret.nil?
159
- @included_templates ||= {}
160
- @included_templates[resource] ||= 0
161
- @included_templates[resource] += 1
162
- end
163
- @template_cache[resource] = ret
164
- ret
165
156
  end
157
+ @template_cache[resource] = ret
158
+ ret
166
159
  end
167
160
  end
168
161
 
@@ -178,7 +171,7 @@ class WikiLinkHandler
178
171
  ret = ""
179
172
  prefix.downcase!
180
173
  case
181
- when FILE_NAMESPACES.include?(prefix)
174
+ when (MEDIA_NAMESPACES+FILE_NAMESPACES).include?(prefix)
182
175
  ret += wiki_image(resource,options)
183
176
  when CATEGORY_NAMESPACES.include?(prefix)
184
177
  self.categories << resource