trac-wiki 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org/'
2
+ gemspec
data/README ADDED
@@ -0,0 +1,40 @@
1
+ = TracWiki =
2
+
3
+ TracWiki is a TracWiki-to-HTML converter for Trac wiki, http://trac.edgewall.org/wiki/WikiFormatting.
4
+
5
+ Project page on github:
6
+
7
+ * http://github.com/vitstradal/trac-wiki
8
+
9
+ == INSTALLATION ==
10
+
11
+ {{{
12
+ gem install trac-wiki
13
+ }}}
14
+
15
+ == SYNOPSIS ==
16
+
17
+ {{{
18
+ require 'trac_wiki'
19
+ html = TracWiki.render('== TracWik text ==')
20
+ }}}
21
+
22
+ == BUGS ==
23
+
24
+ If you found a bug, please report it at the TracWiki project's tracker
25
+ on GitHub:
26
+
27
+ http://github.com/vitstradal/trac-wiki/issues
28
+
29
+ == AUTHORS ==
30
+
31
+ * Vitas Stradal
32
+ Based on Creole:
33
+
34
+ * Lars Christensen (larsch)
35
+ * Daniel Mendler (minad)
36
+
37
+ == LICENSE ==
38
+
39
+
40
+ GPL
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ task :default => :test
2
+
3
+ desc 'Run tests with bacon'
4
+ task :test => FileList['test/*_test.rb'] do |t|
5
+ sh "bacon -q -Ilib:test #{t.prerequisites.join(' ')}"
6
+ end
data/lib/trac_wiki.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'trac_wiki/parser'
2
+ require 'trac_wiki/version'
3
+
4
+ module TracWiki
5
+ # Convert the argument in Trac format to HTML and return the
6
+ # result. Example:
7
+ #
8
+ # TracWiki.creolize("**Hello ''World''**")
9
+ # #=> "<p><strong>Hello <em>World</em></strong></p>"
10
+ #
11
+ # This is an alias for calling Creole#parse:
12
+ # TracWiki.new(text).to_html
13
+ def self.render(text, options = {})
14
+ Parser.new(text, options).to_html
15
+ end
16
+ end
@@ -0,0 +1,402 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+
4
+ # :main: TracWiki
5
+
6
+ # The Creole parses and translates Creole formatted text into
7
+ # XHTML. Creole is a lightweight markup syntax similar to what many
8
+ # WikiWikiWebs use. Example syntax:
9
+ #
10
+ # = Heading 1 =
11
+ # == Heading 2 ==
12
+ # === Heading 3 ===
13
+ # **Bold text**
14
+ # //Italic text//
15
+ # [[Links]]
16
+ # |=Table|=Heading|
17
+ # |Table |Cells |
18
+ # {{image.png}}
19
+ #
20
+ # The simplest interface is TracWiki.render. The default handling of
21
+ # links allow explicit local links using the [[link]] syntax. External
22
+ # links will only be allowed if specified using http(s) and ftp(s)
23
+ # schemes. If special link handling is needed, such as inter-wiki or
24
+ # hierachical local links, you must inherit Creole::CreoleParser and
25
+ # override make_local_link.
26
+ #
27
+ # You can customize the created image markup by overriding
28
+ # make_image.
29
+
30
+ # Main TracWiki parser class. Call TracWikiParser#parse to parse
31
+ # TracWiki formatted text.
32
+ #
33
+ # This class is not reentrant. A separate instance is needed for
34
+ # each thread that needs to convert Creole to HTML.
35
+ #
36
+ # Inherit this to provide custom handling of links. The overrideable
37
+ # methods are: make_local_link
38
+ module TracWiki
39
+ class Parser
40
+
41
+ # Allowed url schemes
42
+ # Examples: http https ftp ftps
43
+ attr_accessor :allowed_schemes
44
+
45
+ # Non-standard wiki text extensions enabled?
46
+ # E.g. underlined, deleted text etc
47
+ attr_writer :extensions
48
+ def extensions?; @extensions; end
49
+
50
+ # Disable url escaping for local links
51
+ # Escaping: [[/Test]] --> %2FTest
52
+ # No escaping: [[/Test]] --> Test
53
+ attr_writer :no_escape
54
+ def no_escape?; @no_escape; end
55
+
56
+ # Create a new Parser instance.
57
+ def initialize(text, options = {})
58
+ @allowed_schemes = %w(http https ftp ftps)
59
+ @text = text
60
+ @extensions = @no_escape = nil
61
+ options.each_pair {|k,v| send("#{k}=", v) }
62
+ end
63
+
64
+ # Convert CCreole text to HTML and return
65
+ # the result. The resulting HTML does not contain <html> and
66
+ # <body> tags.
67
+ #
68
+ # Example:
69
+ #
70
+ # parser = CreoleParser.new("**Hello //World//**", :extensions => true)
71
+ # parser.to_html
72
+ # #=> "<p><strong>Hello <em>World</em></strong></p>"
73
+ def to_html
74
+ @out = ''
75
+ @p = false
76
+ @stack = []
77
+ parse_block(@text)
78
+ @out
79
+ end
80
+
81
+ protected
82
+
83
+ # Escape any characters with special meaning in HTML using HTML
84
+ # entities.
85
+ def escape_html(string)
86
+ CGI::escapeHTML(string)
87
+ end
88
+
89
+ # Escape any characters with special meaning in URLs using URL
90
+ # encoding.
91
+ def escape_url(string)
92
+ CGI::escape(string)
93
+ end
94
+
95
+ def start_tag(tag)
96
+ @stack.push(tag)
97
+ @out << '<' << tag << '>'
98
+ end
99
+
100
+ def end_tag
101
+ @out << '</' << @stack.pop << '>'
102
+ end
103
+
104
+ def toggle_tag(tag, match)
105
+ if @stack.include?(tag)
106
+ if @stack.last == tag
107
+ end_tag
108
+ else
109
+ @out << escape_html(match)
110
+ end
111
+ else
112
+ start_tag(tag)
113
+ end
114
+ end
115
+
116
+ def end_paragraph
117
+ end_tag while !@stack.empty?
118
+ @p = false
119
+ end
120
+
121
+ def start_paragraph
122
+ if @p
123
+ @out << ' ' if @out[-1] != ?\s
124
+ else
125
+ end_paragraph
126
+ start_tag('p')
127
+ @p = true
128
+ end
129
+ end
130
+
131
+ # Translate an explicit local link to a desired URL that is
132
+ # properly URL-escaped. The default behaviour is to convert local
133
+ # links directly, escaping any characters that have special
134
+ # meaning in URLs. Relative URLs in local links are not handled.
135
+ #
136
+ # Examples:
137
+ #
138
+ # make_local_link("LocalLink") #=> "LocalLink"
139
+ # make_local_link("/Foo/Bar") #=> "%2FFoo%2FBar"
140
+ #
141
+ # Must ensure that the result is properly URL-escaped. The caller
142
+ # will handle HTML escaping as necessary. HTML links will not be
143
+ # inserted if the function returns nil.
144
+ #
145
+ # Example custom behaviour:
146
+ #
147
+ # make_local_link("LocalLink") #=> "/LocalLink"
148
+ # make_local_link("Wikipedia:Bread") #=> "http://en.wikipedia.org/wiki/Bread"
149
+ def make_local_link(link) #:doc:
150
+ no_escape? ? link : escape_url(link)
151
+ end
152
+
153
+ # Sanatize a direct url (e.g. http://wikipedia.org/). The default
154
+ # behaviour returns the original link as-is.
155
+ #
156
+ # Must ensure that the result is properly URL-escaped. The caller
157
+ # will handle HTML escaping as necessary. Links will not be
158
+ # converted to HTML links if the function returns link.
159
+ #
160
+ # Custom versions of this function in inherited classes can
161
+ # implement specific link handling behaviour, such as redirection
162
+ # to intermediate pages (for example, for notifing the user that
163
+ # he is leaving the site).
164
+ def make_direct_link(url) #:doc:
165
+ url
166
+ end
167
+
168
+ # Sanatize and prefix image URLs. When images are encountered in
169
+ # Creole text, this function is called to obtain the actual URL of
170
+ # the image. The default behaviour is to return the image link
171
+ # as-is. No image tags are inserted if the function returns nil.
172
+ #
173
+ # Custom version of the method can be used to sanatize URLs
174
+ # (e.g. remove query-parts), inhibit off-site images, or add a
175
+ # base URL, for example:
176
+ #
177
+ # def make_image_link(url)
178
+ # URI.join("http://mywiki.org/images/", url)
179
+ # end
180
+ def make_image_link(url) #:doc:
181
+ url
182
+ end
183
+
184
+ # Create image markup. This
185
+ # method can be overridden to generate custom
186
+ # markup, for example to add html additional attributes or
187
+ # to put divs around the imgs.
188
+ def make_image(uri, alt)
189
+ if alt
190
+ '<img src="' << escape_html(uri) << '" alt="' << escape_html(alt) << '"/>'
191
+ else
192
+ '<img src="' << escape_html(uri) << '"/>'
193
+ end
194
+ end
195
+
196
+ def make_headline(level, text)
197
+ "<h#{level}>" << escape_html(text) << "</h#{level}>"
198
+ end
199
+
200
+ def make_explicit_link(link)
201
+ begin
202
+ uri = URI.parse(link)
203
+ return uri.to_s if uri.scheme && @allowed_schemes.include?(uri.scheme)
204
+ rescue URI::InvalidURIError
205
+ end
206
+ make_local_link(link)
207
+ end
208
+
209
+ def parse_inline(str)
210
+ until str.empty?
211
+ case str
212
+ when /\A(\~)?((https?|ftps?):\/\/\S+?)(?=([\,.?!:;"'\)]+)?(\s|$))/
213
+ str = $'
214
+ if $1
215
+ @out << escape_html($2)
216
+ else
217
+ if uri = make_direct_link($2)
218
+ @out << '<a href="' << escape_html(uri) << '">' << escape_html($2) << '</a>'
219
+ else
220
+ @out << escape_html($&)
221
+ end
222
+ end
223
+ when /\A\[\[\s*([^|]*?)\s*(\|\s*(.*?))?\s*\]\]/m
224
+ str = $'
225
+ link, content = $1, $3
226
+ if uri = make_explicit_link(link)
227
+ @out << '<a href="' << escape_html(uri) << '">'
228
+ if content
229
+ until content.empty?
230
+ content = parse_inline_tag(content)
231
+ end
232
+ else
233
+ @out << escape_html(link)
234
+ end
235
+ @out << '</a>'
236
+ else
237
+ @out << escape_html($&)
238
+ end
239
+ else
240
+ str = parse_inline_tag(str)
241
+ end
242
+ end
243
+ end
244
+
245
+ def parse_inline_tag(str)
246
+ case str
247
+ when /\A\{\{\{(.*?\}*)\}\}\}/ # inline pre (tt)
248
+ @out << '<tt>' << escape_html($1) << '</tt>'
249
+ when /\A`(.*?)`/ # inline pre (tt)
250
+ @out << '<tt>' << escape_html($1) << '</tt>'
251
+ when /\A\{\{\s*(.*?)\s*(\|\s*(.*?)\s*)?\}\}/
252
+ if uri = make_image_link($1)
253
+ @out << make_image(uri, $3)
254
+ else
255
+ @out << escape_html($&)
256
+ end # link
257
+ when /\A([:alpha:]|[:digit:])+/
258
+ @out << $& # word
259
+ when /\A\s+/
260
+ @out << ' ' if @out[-1] != ?\s # spaces
261
+ when /\A\*\*/
262
+ toggle_tag 'strong', $& # bold
263
+ when /\A''/
264
+ toggle_tag 'em', $& # italic
265
+ when /\A\\\\/
266
+ @out << '<br/>' # newline
267
+ when /\A__/
268
+ toggle_tag 'u', $& # underline
269
+ when /\A~~/
270
+ toggle_tag 'del', $& # delete
271
+ # when /\A\+\+/
272
+ # toggle_tag 'ins', $& # insert
273
+ when /\A\^\^/
274
+ toggle_tag 'sup', $& # ^{}
275
+ when /\A,,/
276
+ toggle_tag 'sub', $& # _{}
277
+ when /\A\(R\)/i
278
+ @out << '&#174;' # (R)
279
+ when /\A\(C\)/i
280
+ @out << '&#169;' # (C)
281
+ when /\A!([^\s])/
282
+ @out << escape_html($1) # !neco
283
+ when /./
284
+ @out << escape_html($&) # ordinal char
285
+ end
286
+ return $'
287
+ end
288
+
289
+ def parse_table_row(str)
290
+ @out << '<tr>'
291
+ #str.scan(/\s*\|\|(=)?\s*((\[\[.*?\]\]|\{\{.*?\}\}|[^|~]|~.)*)(?=\||$)/) do
292
+ str.scan(/\s*\|\|(=)?(\s*)(.*?)(?==?\|\||$)/) do
293
+ if !$3.empty? || !$'.empty?
294
+ tag = $1 ? 'th' : 'td'
295
+ le = $2.size
296
+ txt = $3
297
+ style =''
298
+ if txt =~ /\S(\s*)$/
299
+ ri = $1.size
300
+ # style = " style='text-align:left'" if le == 0
301
+ style = " style='text-align:right'" if ri == 0 && le >= 1
302
+ style = " style='text-align:center'" if le >= 2 && ri >= 2
303
+ #print "le#{le} ri#{ri} st:#{style}\n"
304
+ end
305
+ @out << ('<' + tag + style + '>' )
306
+ parse_inline(txt.strip) if txt
307
+ end_tag while @stack.last != 'table'
308
+ @out << ('</' + tag + '>')
309
+ end
310
+ end
311
+ @out << '</tr>'
312
+ end
313
+
314
+ def make_nowikiblock(input)
315
+ input.gsub(/^ (?=\}\}\})/, '')
316
+ end
317
+
318
+ def ulol?(x); x == 'ul' || x == 'ol'; end
319
+
320
+ def parse_block(str)
321
+ until str.empty?
322
+ case str
323
+
324
+ # pre {{{ ... }}}
325
+ when /\A\{\{\{\r?\n(.*?)\r?\n\}\}\}/m
326
+ end_paragraph
327
+ nowikiblock = make_nowikiblock($1)
328
+ @out << '<pre>' << escape_html(nowikiblock) << '</pre>'
329
+
330
+ # horizontal rule
331
+ when /\A\s*-{4,}\s*$/
332
+ end_paragraph
333
+ @out << '<hr/>'
334
+
335
+ # heading == Wiki Ruless ==
336
+ when /\A\s*(={1,6})\s*(.*?)\s*=*\s*$(\r?\n)?/
337
+ end_paragraph
338
+ level = $1.size
339
+ @out << make_headline(level, $2)
340
+
341
+ # table row
342
+ when /\A[ \t]*\|\|.*$(\r?\n)?/
343
+ if !@stack.include?('table')
344
+ end_paragraph
345
+ start_tag('table')
346
+ end
347
+ parse_table_row($&)
348
+
349
+ # empty line
350
+ when /\A\s*$(\r?\n)?/
351
+ end_paragraph
352
+
353
+ # li
354
+ when /\A(\s*([*#]+)\s*(.*?))$(\r?\n)?/
355
+ line, bullet, item = $1, $2, $3
356
+ tag = (bullet[0,1] == '*' ? 'ul' : 'ol')
357
+ if bullet[0,1] == '#' || bullet.size != 2 || @stack.find {|x| ulol?(x) }
358
+ count = @stack.select { |x| ulol?(x) }.size
359
+
360
+ while !@stack.empty? && count > bullet.size
361
+ count -= 1 if ulol?(@stack.last)
362
+ end_tag
363
+ end
364
+
365
+ end_tag while !@stack.empty? && @stack.last != 'li'
366
+
367
+ if @stack.last == 'li' && count == bullet.size
368
+ end_tag
369
+ if @stack.last != tag
370
+ end_tag
371
+ count -= 1
372
+ end
373
+ end
374
+
375
+ while count < bullet.size
376
+ start_tag tag
377
+ count += 1
378
+ start_tag 'li' if count < bullet.size
379
+ end
380
+
381
+ @p = true
382
+ start_tag('li')
383
+ parse_inline(item)
384
+ else
385
+ start_paragraph
386
+ parse_inline(line)
387
+ end
388
+
389
+ # ordinary line
390
+ when /\A([ \t]*\S+.*?)$(\r?\n)?/
391
+ start_paragraph
392
+ parse_inline($1)
393
+ else
394
+ raise "Parse error at #{str[0,30].inspect}"
395
+ end
396
+ str = $'
397
+ end
398
+ end_paragraph
399
+ @out
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,3 @@
1
+ module TracWiki
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,665 @@
1
+ require 'trac_wiki'
2
+
3
+ class Bacon::Context
4
+ def tc(html, wiki, options = {})
5
+ TracWiki.render(wiki, options).should.equal html
6
+ end
7
+
8
+ def tce(html, wiki)
9
+ tc(html, wiki, :extensions => true)
10
+ end
11
+ end
12
+
13
+ describe TracWiki::Parser do
14
+ it 'should parse bold' do
15
+ # Bold can be used inside paragraphs
16
+ tc "<p>This <strong>is</strong> bold</p>", "This **is** bold"
17
+ tc "<p>This <strong>is</strong> bold and <strong>bold</strong>ish</p>", "This **is** bold and **bold**ish"
18
+
19
+ # Bold can be used inside list items
20
+ tc "<ul><li>This is <strong>bold</strong></li></ul>", "* This is **bold**"
21
+
22
+ # Bold can be used inside table cells
23
+ tc("<table><tr><td>This is <strong>bold</strong></td></tr></table>",
24
+ "||This is **bold**||")
25
+
26
+ # Links can appear inside bold text:
27
+ tc("<p>A bold link: <strong><a href=\"http://example.org/\">http://example.org/</a> nice! </strong></p>",
28
+ "A bold link: **http://example.org/ nice! **")
29
+
30
+ # Bold will end at the end of paragraph
31
+ tc "<p>This <strong>is bold</strong></p>", "This **is bold"
32
+
33
+ # Bold will end at the end of list items
34
+ tc("<ul><li>Item <strong>bold</strong></li><li>Item normal</li></ul>",
35
+ "* Item **bold\n* Item normal")
36
+
37
+ # Bold will end at the end of table cells
38
+ tc("<table><tr><td>Item <strong>bold</strong></td><td>Another <strong>bold</strong></td></tr></table>",
39
+ "||Item **bold||Another **bold||")
40
+
41
+ # Bold should not cross paragraphs
42
+ tc("<p>This <strong>is</strong></p><p>bold<strong> maybe</strong></p>",
43
+ "This **is\n\nbold** maybe")
44
+
45
+ # Bold should be able to cross lines
46
+ tc "<p>This <strong>is bold</strong></p>", "This **is\nbold**"
47
+ end
48
+
49
+ it 'should parse italic' do
50
+ # Italic can be used inside paragraphs
51
+ tc("<p>This <em>is</em> italic</p>",
52
+ "This ''is'' italic")
53
+ tc("<p>This <em>is</em> italic and <em>italic</em>ish</p>",
54
+ "This ''is'' italic and ''italic''ish")
55
+
56
+ # Italic can be used inside list items
57
+ tc "<ul><li>This is <em>italic</em></li></ul>", "* This is ''italic''"
58
+
59
+ # Italic can be used inside table cells
60
+ tc("<table><tr><td>This is <em>italic</em></td></tr></table>",
61
+ "||This is ''italic''||")
62
+
63
+ # Links can appear inside italic text:
64
+ tc("<p>A italic link: <em><a href=\"http://example.org/\">http://example.org/</a> nice! </em></p>",
65
+ "A italic link: ''http://example.org/ nice! ''")
66
+
67
+ # Italic will end at the end of paragraph
68
+ tc "<p>This <em>is italic</em></p>", "This ''is italic"
69
+
70
+ # Italic will end at the end of list items
71
+ tc("<ul><li>Item <em>italic</em></li><li>Item normal</li></ul>",
72
+ "* Item ''italic\n* Item normal")
73
+
74
+ # Italic will end at the end of table cells
75
+ tc("<table><tr><td>Item <em>italic</em></td><td>Another <em>italic</em></td></tr></table>",
76
+ "||Item ''italic||Another ''italic")
77
+
78
+ # Italic should not cross paragraphs
79
+ tc("<p>This <em>is</em></p><p>italic<em> maybe</em></p>",
80
+ "This ''is\n\nitalic'' maybe")
81
+
82
+ # Italic should be able to cross lines
83
+ tc "<p>This <em>is italic</em></p>", "This ''is\nitalic''"
84
+ end
85
+
86
+ it 'should parse bold italics' do
87
+ # By example
88
+ tc "<p><strong><em>bold italics</em></strong></p>", "**''bold italics''**"
89
+
90
+ # By example
91
+ tc "<p><em><strong>bold italics</strong></em></p>", "''**bold italics**''"
92
+
93
+ # By example
94
+ tc "<p><em>This is <strong>also</strong> good.</em></p>", "''This is **also** good.''"
95
+ end
96
+
97
+ it 'should parse headings' do
98
+ # Only three differed sized levels of heading are required.
99
+ tc "<h1>Heading 1</h1>", "= Heading 1 ="
100
+ tc "<h2>Heading 2</h2>", "== Heading 2 =="
101
+ tc "<h3>Heading 3</h3>", "=== Heading 3 ==="
102
+ # WARNING: Optional feature, not specified in
103
+ tc "<h4>Heading 4</h4>", "==== Heading 4 ===="
104
+ tc "<h5>Heading 5</h5>", "===== Heading 5 ====="
105
+ tc "<h6>Heading 6</h6>", "====== Heading 6 ======"
106
+
107
+ # Closing (right-side) equal signs are optional
108
+ tc "<h1>Heading 1</h1>", "=Heading 1"
109
+ tc "<h2>Heading 2</h2>", "== Heading 2"
110
+ tc "<h3>Heading 3</h3>", " === Heading 3"
111
+
112
+ # Closing (right-side) equal signs don't need to be balanced and don't impact the kind of heading generated
113
+ tc "<h1>Heading 1</h1>", "=Heading 1 ==="
114
+ tc "<h2>Heading 2</h2>", "== Heading 2 ="
115
+ tc "<h3>Heading 3</h3>", " === Heading 3 ==========="
116
+
117
+ # Whitespace is allowed before the left-side equal signs.
118
+ tc "<h1>Heading 1</h1>", " \t= Heading 1 ="
119
+ tc "<h2>Heading 2</h2>", " \t== Heading 2 =="
120
+
121
+ # Only white-space characters are permitted after the closing equal signs.
122
+ tc "<h1>Heading 1</h1>", " = Heading 1 = "
123
+ tc "<h2>Heading 2</h2>", " == Heading 2 == \t "
124
+
125
+ # WARNING: !! XXX doesn't specify if text after closing equal signs
126
+ # !!becomes part of the heading or invalidates the entire heading.
127
+ # tc "<p> == Heading 2 == foo</p>", " == Heading 2 == foo"
128
+ tc "<h2>Heading 2 == foo</h2>", " == Heading 2 == foo"
129
+
130
+ # Line must start with equal sign
131
+ tc "<p>foo = Heading 1 =</p>", "foo = Heading 1 ="
132
+ end
133
+
134
+ it 'should parse links' do
135
+ # Links
136
+ tc "<p><a href=\"link\">link</a></p>", "[[link]]"
137
+
138
+ # Links can appear in paragraphs (i.e. inline item)
139
+ tc "<p>Hello, <a href=\"world\">world</a></p>", "Hello, [[world]]"
140
+
141
+ # Named links
142
+ tc "<p><a href=\"MyBigPage\">Go to my page</a></p>", "[[MyBigPage|Go to my page]]"
143
+
144
+ # URLs
145
+ tc "<p><a href=\"http://www.example.org/\">http://www.example.org/</a></p>", "[[http://www.example.org/]]"
146
+
147
+ # Single punctuation characters at the end of URLs
148
+ # should not be considered a part of the URL.
149
+ [',','.','?','!',':',';','\'','"'].each do |punct|
150
+ esc_punct = CGI::escapeHTML(punct)
151
+ tc "<p><a href=\"http://www.example.org/\">http://www.example.org/</a>#{esc_punct}</p>", "http://www.example.org/#{punct}"
152
+ end
153
+ # Nameds URLs (by example)
154
+ tc("<p><a href=\"http://www.example.org/\">Visit the Example website</a></p>",
155
+ "[[http://www.example.org/|Visit the Example website]]")
156
+
157
+ # WRNING: Parsing markup within a link is optional
158
+ tc "<p><a href=\"Weird+Stuff\"><strong>Weird</strong> <em>Stuff</em></a></p>", "[[Weird Stuff|**Weird** ''Stuff'']]"
159
+ tc("<p><a href=\"http://example.org/\"><img src=\"image.jpg\"/></a></p>", "[[http://example.org/|{{image.jpg}}]]")
160
+
161
+ # Inside bold
162
+ tc "<p><strong><a href=\"link\">link</a></strong></p>", "**[[link]]**"
163
+
164
+ # Whitespace inside [[ ]] should be ignored
165
+ tc("<p><a href=\"link\">link</a></p>", "[[ link ]]")
166
+ tc("<p><a href=\"link+me\">link me</a></p>", "[[ link me ]]")
167
+ tc("<p><a href=\"http://dot.com/\">dot.com</a></p>", "[[ http://dot.com/ \t| \t dot.com ]]")
168
+ tc("<p><a href=\"http://dot.com/\">dot.com</a></p>", "[[ http://dot.com/ | dot.com ]]")
169
+ end
170
+
171
+ it 'should parse freestanding urls' do
172
+ # Free-standing URL's should be turned into links
173
+ tc "<p><a href=\"http://www.example.org/\">http://www.example.org/</a></p>", "http://www.example.org/"
174
+
175
+ # URL ending in .
176
+ tc "<p>Text <a href=\"http://example.org\">http://example.org</a>. other text</p>", "Text http://example.org. other text"
177
+
178
+ # URL ending in ),
179
+ tc "<p>Text (<a href=\"http://example.org\">http://example.org</a>), other text</p>", "Text (http://example.org), other text"
180
+
181
+ # URL ending in ).
182
+ tc "<p>Text (<a href=\"http://example.org\">http://example.org</a>). other text</p>", "Text (http://example.org). other text"
183
+
184
+ # URL ending in ).
185
+ tc "<p>Text (<a href=\"http://example.org\">http://example.org</a>).</p>", "Text (http://example.org)."
186
+
187
+ # URL ending in )
188
+ tc "<p>Text (<a href=\"http://example.org\">http://example.org</a>)</p>", "Text (http://example.org)"
189
+ end
190
+
191
+ it 'should parse paragraphs' do
192
+ # One or more blank lines end paragraphs.
193
+ tc "<p>This is my text.</p><p>This is more text.</p>", "This is\nmy text.\n\nThis is\nmore text."
194
+ tc "<p>This is my text.</p><p>This is more text.</p>", "This is\nmy text.\n\n\nThis is\nmore text."
195
+ tc "<p>This is my text.</p><p>This is more text.</p>", "This is\nmy text.\n\n\n\nThis is\nmore text."
196
+
197
+ # A list end paragraphs too.
198
+ tc "<p>Hello</p><ul><li>Item</li></ul>", "Hello\n* Item\n"
199
+
200
+ # A table end paragraphs too.
201
+ tc "<p>Hello</p><table><tr><td>Cell</td></tr></table>", "Hello\n||Cell||"
202
+
203
+ # A nowiki end paragraphs too.
204
+ tc "<p>Hello</p><pre>nowiki</pre>", "Hello\n{{{\nnowiki\n}}}\n"
205
+
206
+ # WARNING: A heading ends a paragraph (not specced)
207
+ tc "<p>Hello</p><h1>Heading</h1>", "Hello\n= Heading =\n"
208
+ end
209
+
210
+ it 'should parse linebreaks' do
211
+ # \\ (wiki-style) for line breaks.
212
+ tc "<p>This is the first line,<br/>and this is the second.</p>", "This is the first line,\\\\and this is the second."
213
+ end
214
+
215
+ it 'should parse unordered_lists' do
216
+ # List items begin with a * at the beginning of a line.
217
+ # An item ends at the next *
218
+ tc "<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>", "* Item 1\n *Item 2\n *\t\tItem 3\n"
219
+
220
+ # Whitespace is optional before and after the *.
221
+ tc("<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>",
222
+ " * Item 1\n*Item 2\n \t*\t\tItem 3\n")
223
+
224
+ # A space is required if if the list element starts with bold text.
225
+ tc("<ul><li><ul><li><ul><li>Item 1</li></ul></li></ul></li></ul>", "***Item 1")
226
+ tc("<ul><li><strong>Item 1</strong></li></ul>", "* **Item 1")
227
+
228
+ # An item ends at blank line
229
+ tc("<ul><li>Item</li></ul><p>Par</p>", "* Item\n\nPar\n")
230
+
231
+ # An item ends at a heading
232
+ tc("<ul><li>Item</li></ul><h1>Heading</h1>", "* Item\n= Heading =\n")
233
+
234
+ # An item ends at a table
235
+ tc("<ul><li>Item</li></ul><table><tr><td>Cell</td></tr></table>", "* Item\n||Cell||\n")
236
+
237
+ # An item ends at a nowiki block
238
+ tc("<ul><li>Item</li></ul><pre>Code</pre>", "* Item\n{{{\nCode\n}}}\n")
239
+
240
+ # An item can span multiple lines
241
+ tc("<ul><li>The quick brown fox jumps over lazy dog.</li><li>Humpty Dumpty sat on a wall.</li></ul>",
242
+ "* The quick\nbrown fox\n\tjumps over\nlazy dog.\n*Humpty Dumpty\nsat\t\non a wall.")
243
+
244
+ # An item can contain line breaks
245
+ tc("<ul><li>The quick brown<br/>fox jumps over lazy dog.</li></ul>",
246
+ "* The quick brown\\\\fox jumps over lazy dog.")
247
+
248
+ # Nested
249
+ tc "<ul><li>Item 1<ul><li>Item 2</li></ul></li><li>Item 3</li></ul>", "* Item 1\n **Item 2\n *\t\tItem 3\n"
250
+
251
+ # Nested up to 5 levels
252
+ tc("<ul><li>Item 1<ul><li>Item 2<ul><li>Item 3<ul><li>Item 4<ul><li>Item 5</li></ul></li></ul></li></ul></li></ul></li></ul>",
253
+ "*Item 1\n**Item 2\n***Item 3\n****Item 4\n*****Item 5\n")
254
+
255
+ # ** immediatly following a list element will be treated as a nested unordered element.
256
+ tc("<ul><li>Hello, World!<ul><li>Not bold</li></ul></li></ul>",
257
+ "*Hello,\nWorld!\n**Not bold\n")
258
+
259
+ # ** immediatly following a list element will be treated as a nested unordered element.
260
+ tc("<ol><li>Hello, World!<ul><li>Not bold</li></ul></li></ol>",
261
+ "#Hello,\nWorld!\n**Not bold\n")
262
+
263
+ # [...] otherwise it will be treated as the beginning of bold text.
264
+ tc("<ul><li>Hello, World!</li></ul><p><strong>Not bold</strong></p>",
265
+ "*Hello,\nWorld!\n\n**Not bold\n")
266
+ end
267
+
268
+ it 'should parse ordered lists' do
269
+ # List items begin with a * at the beginning of a line.
270
+ # An item ends at the next *
271
+ tc "<ol><li>Item 1</li><li>Item 2</li><li>Item 3</li></ol>", "# Item 1\n #Item 2\n #\t\tItem 3\n"
272
+
273
+ # Whitespace is optional before and after the #.
274
+ tc("<ol><li>Item 1</li><li>Item 2</li><li>Item 3</li></ol>",
275
+ " # Item 1\n#Item 2\n \t#\t\tItem 3\n")
276
+
277
+ # A space is required if if the list element starts with bold text.
278
+ tc("<ol><li><ol><li><ol><li>Item 1</li></ol></li></ol></li></ol>", "###Item 1")
279
+ tc("<ol><li><strong>Item 1</strong></li></ol>", "# **Item 1")
280
+
281
+ # An item ends at blank line
282
+ tc("<ol><li>Item</li></ol><p>Par</p>", "# Item\n\nPar\n")
283
+
284
+ # An item ends at a heading
285
+ tc("<ol><li>Item</li></ol><h1>Heading</h1>", "# Item\n= Heading =\n")
286
+
287
+ # An item ends at a table
288
+ tc("<ol><li>Item</li></ol><table><tr><td>Cell</td></tr></table>", "# Item\n||Cell||\n")
289
+
290
+ # An item ends at a nowiki block
291
+ tc("<ol><li>Item</li></ol><pre>Code</pre>", "# Item\n{{{\nCode\n}}}\n")
292
+
293
+ # An item can span multiple lines
294
+ tc("<ol><li>The quick brown fox jumps over lazy dog.</li><li>Humpty Dumpty sat on a wall.</li></ol>",
295
+ "# The quick\nbrown fox\n\tjumps over\nlazy dog.\n#Humpty Dumpty\nsat\t\non a wall.")
296
+
297
+ # An item can contain line breaks
298
+ tc("<ol><li>The quick brown<br/>fox jumps over lazy dog.</li></ol>",
299
+ "# The quick brown\\\\fox jumps over lazy dog.")
300
+
301
+ # Nested
302
+ tc "<ol><li>Item 1<ol><li>Item 2</li></ol></li><li>Item 3</li></ol>", "# Item 1\n ##Item 2\n #\t\tItem 3\n"
303
+
304
+ # Nested up to 5 levels
305
+ tc("<ol><li>Item 1<ol><li>Item 2<ol><li>Item 3<ol><li>Item 4<ol><li>Item 5</li></ol></li></ol></li></ol></li></ol></li></ol>",
306
+ "#Item 1\n##Item 2\n###Item 3\n####Item 4\n#####Item 5\n")
307
+
308
+ # The two-bullet rule only applies to **.
309
+ tc("<ol><li><ol><li>Item</li></ol></li></ol>", "##Item")
310
+ end
311
+
312
+ it 'should parse ordered lists #2' do
313
+ tc "<ol><li>Item 1</li><li>Item 2</li><li>Item 3</li></ol>", "# Item 1\n #Item 2\n #\t\tItem 3\n"
314
+ # Nested
315
+ tc "<ol><li>Item 1<ol><li>Item 2</li></ol></li><li>Item 3</li></ol>", "# Item 1\n ##Item 2\n #\t\tItem 3\n"
316
+ # Multiline
317
+ tc "<ol><li>Item 1 on multiple lines</li></ol>", "# Item 1\non multiple lines"
318
+ end
319
+
320
+ it 'should parse ambiguious mixed lists' do
321
+ # ol following ul
322
+ tc("<ul><li>uitem</li></ul><ol><li>oitem</li></ol>", "*uitem\n#oitem\n")
323
+
324
+ # ul following ol
325
+ tc("<ol><li>uitem</li></ol><ul><li>oitem</li></ul>", "#uitem\n*oitem\n")
326
+
327
+ # 2ol following ul
328
+ tc("<ul><li>uitem<ol><li>oitem</li></ol></li></ul>", "*uitem\n##oitem\n")
329
+
330
+ # 2ul following ol
331
+ tc("<ol><li>uitem<ul><li>oitem</li></ul></li></ol>", "#uitem\n**oitem\n")
332
+
333
+ # 3ol following 3ul
334
+ tc("<ul><li><ul><li><ul><li>uitem</li></ul><ol><li>oitem</li></ol></li></ul></li></ul>", "***uitem\n###oitem\n")
335
+
336
+ # 2ul following 2ol
337
+ tc("<ol><li><ol><li>uitem</li></ol><ul><li>oitem</li></ul></li></ol>", "##uitem\n**oitem\n")
338
+
339
+ # ol following 2ol
340
+ tc("<ol><li><ol><li>oitem1</li></ol></li><li>oitem2</li></ol>", "##oitem1\n#oitem2\n")
341
+ # ul following 2ol
342
+ tc("<ol><li><ol><li>oitem1</li></ol></li></ol><ul><li>oitem2</li></ul>", "##oitem1\n*oitem2\n")
343
+ end
344
+
345
+ it 'should parse ambiguious italics and url' do
346
+ # Uncommon URL schemes should not be parsed as URLs
347
+ tc("<p>This is what can go wrong:<em>this should be an italic text</em>.</p>",
348
+ "This is what can go wrong:''this should be an italic text''.")
349
+
350
+ # A link inside italic text
351
+ tc("<p>How about <em>a link, like <a href=\"http://example.org\">http://example.org</a>, in italic</em> text?</p>",
352
+ "How about ''a link, like http://example.org, in italic'' text?")
353
+
354
+ # Another test
355
+ tc("<p>Formatted fruits, for example:<em>apples</em>, oranges, <strong>pears</strong> ...</p>",
356
+ "Formatted fruits, for example:''apples'', oranges, **pears** ...")
357
+ end
358
+
359
+ it 'should parse ambiguious bold and lists' do
360
+ tc "<p><strong> bold text </strong></p>", "** bold text **"
361
+ tc "<p> <strong> bold text </strong></p>", " ** bold text **"
362
+ end
363
+
364
+ it 'should parse nowiki' do
365
+ # ... works as block
366
+ tc "<pre>Hello</pre>", "{{{\nHello\n}}}\n"
367
+
368
+ # ... works inline
369
+ tc "<p>Hello <tt>world</tt>.</p>", "Hello {{{world}}}."
370
+ tc "<p><tt>Hello</tt> <tt>world</tt>.</p>", "{{{Hello}}} {{{world}}}."
371
+
372
+ # No wiki markup is interpreted inbetween
373
+ tc "<pre>**Hello**</pre>", "{{{\n**Hello**\n}}}\n"
374
+
375
+ # Leading whitespaces are not permitted
376
+ tc("<p> {{{ Hello }}}</p>", " {{{\nHello\n}}}")
377
+ tc("<p>{{{ Hello }}}</p>", "{{{\nHello\n }}}")
378
+
379
+ # Assumed: Should preserve whitespace
380
+ tc("<pre> \t Hello, \t \n \t World \t </pre>",
381
+ "{{{\n \t Hello, \t \n \t World \t \n}}}\n")
382
+
383
+ # In preformatted blocks ... one leading space is removed
384
+ tc("<pre>nowikiblock\n}}}</pre>", "{{{\nnowikiblock\n }}}\n}}}\n")
385
+
386
+ # In inline nowiki, any trailing closing brace is included in the span
387
+ tc("<p>this is <tt>nowiki}</tt></p>", "this is {{{nowiki}}}}")
388
+ tc("<p>this is <tt>nowiki}}</tt></p>", "this is {{{nowiki}}}}}")
389
+ tc("<p>this is <tt>nowiki}}}</tt></p>", "this is {{{nowiki}}}}}}")
390
+ tc("<p>this is <tt>nowiki}}}}</tt></p>", "this is {{{nowiki}}}}}}}")
391
+ end
392
+
393
+ it 'should escape html' do
394
+ # Special HTML chars should be escaped
395
+ tc("<p>&lt;b&gt;not bold&lt;/b&gt;</p>", "<b>not bold</b>")
396
+
397
+ # Image tags should be escape
398
+ tc("<p><img src=\"image.jpg\" alt=\"&quot;tag&quot;\"/></p>", "{{image.jpg|\"tag\"}}")
399
+
400
+ # Malicious links should not be converted.
401
+ tc("<p><a href=\"javascript%3Aalert%28%22Boo%21%22%29\">Click</a></p>", "[[javascript:alert(\"Boo!\")|Click]]")
402
+ end
403
+
404
+ it 'should support character escape' do
405
+ tc "<p>** Not Bold **</p>", "!** Not Bold !**"
406
+ tc "<p>// Not Italic //</p>", "!// Not Italic !//"
407
+ tc "<p>* Not Bullet</p>", "!* Not Bullet"
408
+ # Following char is not a blank (space or line feed)
409
+ tc "<p>Hello ~ world</p>", "Hello ~ world\n"
410
+ tc "<p>Hello ~ world</p>", "Hello ~\nworld\n"
411
+ # Not escaping inside URLs
412
+ tc "<p><a href=\"http://example.org/~user/\">http://example.org/~user/</a></p>", "http://example.org/~user/"
413
+
414
+ # Escaping links
415
+ tc "<p>http://www.example.org/</p>", "~http://www.example.org/"
416
+ end
417
+
418
+ it 'should parse horizontal rule' do
419
+ # Four hyphens make a horizontal rule
420
+ tc "<hr/>", "----"
421
+
422
+ # Whitespace around them is allowed
423
+ tc "<hr/>", " ----"
424
+ tc "<hr/>", "---- "
425
+ tc "<hr/>", " ---- "
426
+ tc "<hr/>", " \t ---- \t "
427
+
428
+ # Nothing else than hyphens and whitespace is "allowed"
429
+ tc "<p>foo ----</p>", "foo ----\n"
430
+ tc "<p>---- foo</p>", "---- foo\n"
431
+
432
+ # [...] no whitespace is allowed between them
433
+ tc "<p> -- -- </p>", " -- -- "
434
+ tc "<p> -- -- </p>", " --\t-- "
435
+ end
436
+
437
+ it 'should parse table' do
438
+ tc "<table><tr><td>Hello, World!</td></tr></table>", "||Hello, World!||"
439
+ tc "<table><tr><td style='text-align:right'>Hello, Right World!</td></tr></table>", "|| Hello, Right World!||"
440
+ tc "<table><tr><th style='text-align:right'>Hello, Right World!</th></tr></table>", "||= Hello, Right World!=||"
441
+ tc "<table><tr><td style='text-align:center'>Hello, Centered World!</td></tr></table>", "|| Hello, Centered World! ||"
442
+ tc "<table><tr><th style='text-align:center'>Hello, Centered World!</th></tr></table>", "||= Hello, Centered World! =||"
443
+ # Multiple columns
444
+ tc "<table><tr><td>c1</td><td>c2</td><td>c3</td></tr></table>", "||c1||c2||c3||"
445
+ # Multiple rows
446
+ tc "<table><tr><td>c11</td><td>c12</td></tr><tr><td>c21</td><td>c22</td></tr></table>", "||c11||c12||\n||c21||c22||\n"
447
+ # End pipe is optional
448
+ tc "<table><tr><td>c1</td><td>c2</td><td>c3</td></tr></table>", "||c1||c2||c3"
449
+ # Empty cells
450
+ tc "<table><tr><td>c1</td><td></td><td>c2</td></tr></table>", "||c1|| ||c2"
451
+ # Escaping cell separator
452
+ tc "<table><tr><td>c1|c2</td><td>c3</td></tr></table>", "||c1!|c2||c3"
453
+ # Escape in last cell + empty cell
454
+ tc "<table><tr><td>c1</td><td>c2|</td></tr></table>", "||c1||c2!|"
455
+ tc "<table><tr><td>c1</td><td>c2|</td></tr></table>", "||c1||c2!|"
456
+ tc "<table><tr><td>c1</td><td>c2|</td><td></td></tr></table>", "||c1||c2| || ||"
457
+ # Equal sign after pipe make a header
458
+ tc "<table><tr><th>Header</th></tr></table>", "||=Header=||"
459
+
460
+ tc "<table><tr><td>c1</td><td><a href=\"Link\">Link text</a></td><td><img src=\"Image\" alt=\"Image text\"/></td></tr></table>", "||c1||[[Link|Link text]]||{{Image|Image text}}||"
461
+ end
462
+
463
+ it 'should parse following table' do
464
+ # table followed by heading
465
+ tc("<table><tr><td>table</td></tr></table><h1>heading</h1>", "||table||\n=heading=\n")
466
+ tc("<table><tr><td>table</td></tr></table><h1>heading</h1>", "||table||\n\n=heading=\n")
467
+ # table followed by paragraph
468
+ tc("<table><tr><td>table</td></tr></table><p>par</p>", "||table||\npar\n")
469
+ tc("<table><tr><td>table</td></tr></table><p>par</p>", "||table||\n\npar\n")
470
+ # table followed by unordered list
471
+ tc("<table><tr><td>table</td></tr></table><ul><li>item</li></ul>", "||table||\n*item\n")
472
+ tc("<table><tr><td>table</td></tr></table><ul><li>item</li></ul>", "||table||\n\n*item\n")
473
+ # table followed by ordered list
474
+ tc("<table><tr><td>table</td></tr></table><ol><li>item</li></ol>", "||table||\n#item\n")
475
+ tc("<table><tr><td>table</td></tr></table><ol><li>item</li></ol>", "||table||\n\n#item\n")
476
+ # table followed by horizontal rule
477
+ tc("<table><tr><td>table</td></tr></table><hr/>", "||table||\n----\n")
478
+ tc("<table><tr><td>table</td></tr></table><hr/>", "||table||\n\n----\n")
479
+ # table followed by nowiki block
480
+ tc("<table><tr><td>table</td></tr></table><pre>pre</pre>", "||table||\n{{{\npre\n}}}\n")
481
+ tc("<table><tr><td>table</td></tr></table><pre>pre</pre>", "||table||\n\n{{{\npre\n}}}\n")
482
+ # table followed by table
483
+ tc("<table><tr><td>table</td></tr><tr><td>table</td></tr></table>", "||table||\n||table||\n")
484
+ tc("<table><tr><td>table</td></tr></table><table><tr><td>table</td></tr></table>", "||table||\n\n||table||\n")
485
+ end
486
+
487
+ it 'should parse following heading' do
488
+ # heading
489
+ tc("<h1>heading1</h1><h1>heading2</h1>", "=heading1=\n=heading2\n")
490
+ tc("<h1>heading1</h1><h1>heading2</h1>", "=heading1=\n\n=heading2\n")
491
+ # paragraph
492
+ tc("<h1>heading</h1><p>par</p>", "=heading=\npar\n")
493
+ tc("<h1>heading</h1><p>par</p>", "=heading=\n\npar\n")
494
+ # unordered list
495
+ tc("<h1>heading</h1><ul><li>item</li></ul>", "=heading=\n*item\n")
496
+ tc("<h1>heading</h1><ul><li>item</li></ul>", "=heading=\n\n*item\n")
497
+ # ordered list
498
+ tc("<h1>heading</h1><ol><li>item</li></ol>", "=heading=\n#item\n")
499
+ tc("<h1>heading</h1><ol><li>item</li></ol>", "=heading=\n\n#item\n")
500
+ # horizontal rule
501
+ tc("<h1>heading</h1><hr/>", "=heading=\n----\n")
502
+ tc("<h1>heading</h1><hr/>", "=heading=\n\n----\n")
503
+ # nowiki block
504
+ tc("<h1>heading</h1><pre>nowiki</pre>", "=heading=\n{{{\nnowiki\n}}}\n")
505
+ tc("<h1>heading</h1><pre>nowiki</pre>", "=heading=\n\n{{{\nnowiki\n}}}\n")
506
+ # table
507
+ tc("<h1>heading</h1><table><tr><td>table</td></tr></table>", "=heading=\n||table||\n")
508
+ tc("<h1>heading</h1><table><tr><td>table</td></tr></table>", "=heading=\n\n||table||\n")
509
+ end
510
+
511
+ it 'should parse following paragraph' do
512
+ # heading
513
+ tc("<p>par</p><h1>heading</h1>", "par\n=heading=")
514
+ tc("<p>par</p><h1>heading</h1>", "par\n\n=heading=")
515
+ # paragraph
516
+ tc("<p>par par</p>", "par\npar\n")
517
+ tc("<p>par</p><p>par</p>", "par\n\npar\n")
518
+ # unordered
519
+ tc("<p>par</p><ul><li>item</li></ul>", "par\n*item")
520
+ tc("<p>par</p><ul><li>item</li></ul>", "par\n\n*item")
521
+ # ordered
522
+ tc("<p>par</p><ol><li>item</li></ol>", "par\n#item\n")
523
+ tc("<p>par</p><ol><li>item</li></ol>", "par\n\n#item\n")
524
+ # horizontal
525
+ tc("<p>par</p><hr/>", "par\n----\n")
526
+ tc("<p>par</p><hr/>", "par\n\n----\n")
527
+ # nowiki
528
+ tc("<p>par</p><pre>nowiki</pre>", "par\n{{{\nnowiki\n}}}\n")
529
+ tc("<p>par</p><pre>nowiki</pre>", "par\n\n{{{\nnowiki\n}}}\n")
530
+ # table
531
+ tc("<p>par</p><table><tr><td>table</td></tr></table>", "par\n||table||\n")
532
+ tc("<p>par</p><table><tr><td>table</td></tr></table>", "par\n\n||table||\n")
533
+ end
534
+
535
+ it 'should parse following unordered list' do
536
+ # heading
537
+ tc("<ul><li>item</li></ul><h1>heading</h1>", "*item\n=heading=")
538
+ tc("<ul><li>item</li></ul><h1>heading</h1>", "*item\n\n=heading=")
539
+ # paragraph
540
+ tc("<ul><li>item par</li></ul>", "*item\npar\n") # items may span multiple lines
541
+ tc("<ul><li>item</li></ul><p>par</p>", "*item\n\npar\n")
542
+ # unordered
543
+ tc("<ul><li>item</li><li>item</li></ul>", "*item\n*item\n")
544
+ tc("<ul><li>item</li></ul><ul><li>item</li></ul>", "*item\n\n*item\n")
545
+ # ordered
546
+ tc("<ul><li>item</li></ul><ol><li>item</li></ol>", "*item\n#item\n")
547
+ tc("<ul><li>item</li></ul><ol><li>item</li></ol>", "*item\n\n#item\n")
548
+ # horizontal rule
549
+ tc("<ul><li>item</li></ul><hr/>", "*item\n----\n")
550
+ tc("<ul><li>item</li></ul><hr/>", "*item\n\n----\n")
551
+ # nowiki
552
+ tc("<ul><li>item</li></ul><pre>nowiki</pre>", "*item\n{{{\nnowiki\n}}}\n")
553
+ tc("<ul><li>item</li></ul><pre>nowiki</pre>", "*item\n\n{{{\nnowiki\n}}}\n")
554
+ # table
555
+ tc("<ul><li>item</li></ul><table><tr><td>table</td></tr></table>", "*item\n||table||\n")
556
+ tc("<ul><li>item</li></ul><table><tr><td>table</td></tr></table>", "*item\n\n||table||\n")
557
+ end
558
+
559
+ it 'should parse following ordered list' do
560
+ # heading
561
+ tc("<ol><li>item</li></ol><h1>heading</h1>", "#item\n=heading=")
562
+ tc("<ol><li>item</li></ol><h1>heading</h1>", "#item\n\n=heading=")
563
+ # paragraph
564
+ tc("<ol><li>item par</li></ol>", "#item\npar\n") # items may span multiple lines
565
+ tc("<ol><li>item</li></ol><p>par</p>", "#item\n\npar\n")
566
+ # unordered
567
+ tc("<ol><li>item</li></ol><ul><li>item</li></ul>", "#item\n*item\n")
568
+ tc("<ol><li>item</li></ol><ul><li>item</li></ul>", "#item\n\n*item\n")
569
+ # ordered
570
+ tc("<ol><li>item</li><li>item</li></ol>", "#item\n#item\n")
571
+ tc("<ol><li>item</li></ol><ol><li>item</li></ol>", "#item\n\n#item\n")
572
+ # horizontal role
573
+ tc("<ol><li>item</li></ol><hr/>", "#item\n----\n")
574
+ tc("<ol><li>item</li></ol><hr/>", "#item\n\n----\n")
575
+ # nowiki
576
+ tc("<ol><li>item</li></ol><pre>nowiki</pre>", "#item\n{{{\nnowiki\n}}}\n")
577
+ tc("<ol><li>item</li></ol><pre>nowiki</pre>", "#item\n\n{{{\nnowiki\n}}}\n")
578
+ # table
579
+ tc("<ol><li>item</li></ol><table><tr><td>table</td></tr></table>", "#item\n||table||\n")
580
+ tc("<ol><li>item</li></ol><table><tr><td>table</td></tr></table>", "#item\n\n||table||\n")
581
+ end
582
+
583
+ it 'should parse following horizontal rule' do
584
+ # heading
585
+ tc("<hr/><h1>heading</h1>", "----\n=heading=")
586
+ tc("<hr/><h1>heading</h1>", "----\n\n=heading=")
587
+ # paragraph
588
+ tc("<hr/><p>par</p>", "----\npar\n")
589
+ tc("<hr/><p>par</p>", "----\n\npar\n")
590
+ # unordered
591
+ tc("<hr/><ul><li>item</li></ul>", "----\n*item")
592
+ tc("<hr/><ul><li>item</li></ul>", "----\n*item")
593
+ # ordered
594
+ tc("<hr/><ol><li>item</li></ol>", "----\n#item")
595
+ tc("<hr/><ol><li>item</li></ol>", "----\n#item")
596
+ # horizontal
597
+ tc("<hr/><hr/>", "----\n----\n")
598
+ tc("<hr/><hr/>", "----\n\n----\n")
599
+ # nowiki
600
+ tc("<hr/><pre>nowiki</pre>", "----\n{{{\nnowiki\n}}}\n")
601
+ tc("<hr/><pre>nowiki</pre>", "----\n\n{{{\nnowiki\n}}}\n")
602
+ # table
603
+ tc("<hr/><table><tr><td>table</td></tr></table>", "----\n||table||\n")
604
+ tc("<hr/><table><tr><td>table</td></tr></table>", "----\n\n||table||\n")
605
+ end
606
+
607
+ it 'should parse following nowiki block' do
608
+ # heading
609
+ tc("<pre>nowiki</pre><h1>heading</h1>", "{{{\nnowiki\n}}}\n=heading=")
610
+ tc("<pre>nowiki</pre><h1>heading</h1>", "{{{\nnowiki\n}}}\n\n=heading=")
611
+ # paragraph
612
+ tc("<pre>nowiki</pre><p>par</p>", "{{{\nnowiki\n}}}\npar")
613
+ tc("<pre>nowiki</pre><p>par</p>", "{{{\nnowiki\n}}}\n\npar")
614
+ # unordered
615
+ tc("<pre>nowiki</pre><ul><li>item</li></ul>", "{{{\nnowiki\n}}}\n*item\n")
616
+ tc("<pre>nowiki</pre><ul><li>item</li></ul>", "{{{\nnowiki\n}}}\n\n*item\n")
617
+ # ordered
618
+ tc("<pre>nowiki</pre><ol><li>item</li></ol>", "{{{\nnowiki\n}}}\n#item\n")
619
+ tc("<pre>nowiki</pre><ol><li>item</li></ol>", "{{{\nnowiki\n}}}\n\n#item\n")
620
+ # horizontal
621
+ tc("<pre>nowiki</pre><hr/>", "{{{\nnowiki\n}}}\n----\n")
622
+ tc("<pre>nowiki</pre><hr/>", "{{{\nnowiki\n}}}\n\n----\n")
623
+ # nowiki
624
+ tc("<pre>nowiki</pre><pre>nowiki</pre>", "{{{\nnowiki\n}}}\n{{{\nnowiki\n}}}\n")
625
+ tc("<pre>nowiki</pre><pre>nowiki</pre>", "{{{\nnowiki\n}}}\n\n{{{\nnowiki\n}}}\n")
626
+ # table
627
+ tc("<pre>nowiki</pre><table><tr><td>table</td></tr></table>", "{{{\nnowiki\n}}}\n||table||\n")
628
+ tc("<pre>nowiki</pre><table><tr><td>table</td></tr></table>", "{{{\nnowiki\n}}}\n\n||table||\n")
629
+ end
630
+
631
+ it 'should parse image' do
632
+ tc("<p><img src=\"image.jpg\"/></p>", "{{image.jpg}}")
633
+ tc("<p><img src=\"image.jpg\" alt=\"tag\"/></p>", "{{image.jpg|tag}}")
634
+ tc("<p><img src=\"http://example.org/image.jpg\"/></p>", "{{http://example.org/image.jpg}}")
635
+ end
636
+
637
+ it 'should parse bold combo' do
638
+ tc("<p><strong>bold and</strong></p><table><tr><td>table</td></tr></table><p>end<strong></strong></p>",
639
+ "**bold and\n||table||\nend**")
640
+ end
641
+
642
+ it 'should support extensions' do
643
+ tce("<p>This is <u>underlined</u></p>",
644
+ "This is __underlined__")
645
+
646
+ tce("<p>This is <del>deleted</del></p>",
647
+ "This is ~~deleted~~")
648
+
649
+ tce("<p>This is <sup>super</sup></p>",
650
+ "This is ^^super^^")
651
+
652
+ tce("<p>This is <sub>sub</sub></p>",
653
+ "This is ,,sub,,")
654
+
655
+ tce("<p>&#174;</p>", "(R)")
656
+ tce("<p>&#174;</p>", "(r)")
657
+ tce("<p>&#169;</p>", "(C)")
658
+ tce("<p>&#169;</p>", "(c)")
659
+ end
660
+
661
+ it 'should support no_escape' do
662
+ tc("<p><a href=\"a%2Fb%2Fc\">a/b/c</a></p>", "[[a/b/c]]")
663
+ tc("<p><a href=\"a/b/c\">a/b/c</a></p>", "[[a/b/c]]", :no_escape => true)
664
+ end
665
+ end
data/trac-wiki.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/lib/trac_wiki/version'
2
+ require 'date'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'trac-wiki'
6
+ s.version = TracWiki::VERSION
7
+ s.date = Date.today.to_s
8
+
9
+ s.authors = ['Vitas Stradal']
10
+ s.email = ['vitas@matfyz.cz' ]
11
+ s.summary = 'Trac Wiki markup language'
12
+ s.description = 'TracWiki markup language render (http://trac.edgewall.org/wiki/WikiFormatting ).'
13
+ s.extra_rdoc_files = %w(README)
14
+ s.rubyforge_project = s.name
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = %w(lib)
19
+
20
+ s.homepage = 'http://github.com/vitstradal/trac-wiki'
21
+
22
+ s.add_development_dependency('bacon')
23
+ s.add_development_dependency('rake')
24
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trac-wiki
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Vitas Stradal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bacon
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: TracWiki markup language render (http://trac.edgewall.org/wiki/WikiFormatting
47
+ ).
48
+ email:
49
+ - vitas@matfyz.cz
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files:
53
+ - README
54
+ files:
55
+ - Gemfile
56
+ - README
57
+ - Rakefile
58
+ - lib/trac_wiki.rb
59
+ - lib/trac_wiki/parser.rb
60
+ - lib/trac_wiki/version.rb
61
+ - test/parser_test.rb
62
+ - trac-wiki.gemspec
63
+ homepage: http://github.com/vitstradal/trac-wiki
64
+ licenses: []
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project: trac-wiki
83
+ rubygems_version: 1.8.23
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Trac Wiki markup language
87
+ test_files: []
88
+ has_rdoc: