trac-wiki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: