RedCloth 3.0.4 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of RedCloth might be problematic. Click here for more details.

Files changed (63) hide show
  1. data/CHANGELOG +17 -0
  2. data/COPYING +18 -0
  3. data/README +156 -0
  4. data/Rakefile +238 -0
  5. data/bin/redcloth +27 -2
  6. data/ext/redcloth_scan/extconf.rb +9 -0
  7. data/ext/redcloth_scan/redcloth.h +149 -0
  8. data/ext/redcloth_scan/redcloth_attributes.c +650 -0
  9. data/ext/redcloth_scan/redcloth_attributes.rl +78 -0
  10. data/ext/redcloth_scan/redcloth_common.rl +113 -0
  11. data/ext/redcloth_scan/redcloth_inline.c +5102 -0
  12. data/ext/redcloth_scan/redcloth_inline.rl +282 -0
  13. data/ext/redcloth_scan/redcloth_scan.c +9300 -0
  14. data/ext/redcloth_scan/redcloth_scan.rl +523 -0
  15. data/extras/mingw-rbconfig.rb +176 -0
  16. data/extras/ragel_profiler.rb +73 -0
  17. data/lib/redcloth.rb +22 -1128
  18. data/lib/redcloth/formatters/base.rb +50 -0
  19. data/lib/redcloth/formatters/html.rb +342 -0
  20. data/lib/redcloth/formatters/latex.rb +227 -0
  21. data/lib/redcloth/formatters/latex_entities.yml +2414 -0
  22. data/lib/redcloth/textile_doc.rb +105 -0
  23. data/lib/redcloth/version.rb +18 -0
  24. data/test/basic.yml +794 -0
  25. data/test/code.yml +195 -0
  26. data/test/definitions.yml +71 -0
  27. data/test/extra_whitespace.yml +64 -0
  28. data/test/filter_html.yml +177 -0
  29. data/test/filter_pba.yml +12 -0
  30. data/test/helper.rb +108 -0
  31. data/test/html.yml +271 -0
  32. data/test/images.yml +202 -0
  33. data/{tests → test}/instiki.yml +14 -15
  34. data/test/links.yml +214 -0
  35. data/test/lists.yml +283 -0
  36. data/test/poignant.yml +89 -0
  37. data/test/sanitize_html.yml +42 -0
  38. data/test/table.yml +267 -0
  39. data/test/test_custom_tags.rb +46 -0
  40. data/test/test_extensions.rb +31 -0
  41. data/test/test_formatters.rb +15 -0
  42. data/test/test_parser.rb +68 -0
  43. data/test/test_restrictions.rb +41 -0
  44. data/test/textism.yml +480 -0
  45. data/test/threshold.yml +772 -0
  46. data/test/validate_fixtures.rb +73 -0
  47. metadata +94 -60
  48. data/doc/CHANGELOG +0 -160
  49. data/doc/COPYING +0 -25
  50. data/doc/README +0 -106
  51. data/doc/REFERENCE +0 -216
  52. data/doc/make.rb +0 -359
  53. data/run-tests.rb +0 -28
  54. data/setup.rb +0 -1376
  55. data/tests/code.yml +0 -105
  56. data/tests/hard_breaks.yml +0 -26
  57. data/tests/images.yml +0 -171
  58. data/tests/links.yml +0 -155
  59. data/tests/lists.yml +0 -77
  60. data/tests/markdown.yml +0 -218
  61. data/tests/poignant.yml +0 -64
  62. data/tests/table.yml +0 -198
  63. data/tests/textism.yml +0 -406
@@ -0,0 +1,50 @@
1
+ module RedCloth::Formatters
2
+ module Base
3
+
4
+ def pba(opts)
5
+ opts.delete(:style) if filter_styles
6
+ opts.delete(:class) if filter_classes
7
+ opts.delete(:id) if filter_ids
8
+
9
+ atts = ''
10
+ opts[:"text-align"] = opts.delete(:align)
11
+ opts[:style] += ';' if opts[:style] && (opts[:style][-1..-1] != ';')
12
+ [:float, :"text-align", :"vertical-align"].each do |a|
13
+ opts[:style] = "#{a}:#{opts[a]};#{opts[:style]}" if opts[a]
14
+ end
15
+ [:"padding-right", :"padding-left"].each do |a|
16
+ opts[:style] = "#{a}:#{opts[a]}em;#{opts[:style]}" if opts[a]
17
+ end
18
+ [:style, :class, :lang, :id, :colspan, :rowspan, :title, :start, :align].each do |a|
19
+ atts << " #{a}=\"#{ opts[a] }\"" if opts[a]
20
+ end
21
+ atts
22
+ end
23
+
24
+ def ignore(opts)
25
+ opts[:text]
26
+ end
27
+ alias_method :notextile, :ignore
28
+
29
+ def redcloth_version(opts)
30
+ p(:text => "#{opts[:prefix]}#{RedCloth::VERSION::STRING}")
31
+ end
32
+
33
+ def inline_redcloth_version(opts)
34
+ RedCloth::VERSION::STRING
35
+ end
36
+
37
+ def method_missing(method, opts)
38
+ opts[:text] || ""
39
+ end
40
+
41
+ def before_transform(text)
42
+
43
+ end
44
+
45
+ def after_transform(text)
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,342 @@
1
+ module RedCloth::Formatters::HTML
2
+ include RedCloth::Formatters::Base
3
+
4
+ # escapement for regular HTML (not in PRE tag)
5
+ def escape(text)
6
+ html_esc(text)
7
+ end
8
+
9
+ # escapement for HTML in a PRE tag
10
+ def escape_pre(text)
11
+ html_esc(text, :html_escape_preformatted)
12
+ end
13
+
14
+ def after_transform(text)
15
+ text.chomp!
16
+ end
17
+
18
+ [:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
19
+ define_method(m) do |opts|
20
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
21
+ end
22
+ end
23
+
24
+ [:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
25
+ define_method(m) do |opts|
26
+ opts[:block] = true
27
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>"
28
+ end
29
+ end
30
+
31
+ def acronym(opts)
32
+ opts[:block] = true
33
+ "<acronym#{pba(opts)}>#{caps(:text => opts[:text])}</acronym>"
34
+ end
35
+
36
+ def caps(opts)
37
+ if no_span_caps
38
+ opts[:text]
39
+ else
40
+ opts[:class] = 'caps'
41
+ span(opts)
42
+ end
43
+ end
44
+
45
+ def del(opts)
46
+ opts[:block] = true
47
+ "<del#{pba(opts)}>#{opts[:text]}</del>"
48
+ end
49
+
50
+ def del_phrase(opts)
51
+ " #{del(opts)}"
52
+ end
53
+
54
+ [:ol, :ul].each do |m|
55
+ define_method("#{m}_open") do |opts|
56
+ opts[:block] = true
57
+ "#{"\n" if opts[:nest] > 1}#{"\t" * (opts[:nest] - 1)}<#{m}#{pba(opts)}>\n"
58
+ end
59
+ define_method("#{m}_close") do |opts|
60
+ "#{li_close}#{"\t" * (opts[:nest] - 1)}</#{m}>#{"\n" if opts[:nest] <= 1}"
61
+ end
62
+ end
63
+
64
+ def li_open(opts)
65
+ "#{li_close unless opts.delete(:first)}#{"\t" * opts[:nest]}<li#{pba(opts)}>#{opts[:text]}"
66
+ end
67
+
68
+ def li_close(opts=nil)
69
+ "</li>\n"
70
+ end
71
+
72
+ def dl_open(opts)
73
+ opts[:block] = true
74
+ "<dl#{pba(opts)}>\n"
75
+ end
76
+
77
+ def dl_close(opts=nil)
78
+ "</dl>\n"
79
+ end
80
+
81
+ [:dt, :dd].each do |m|
82
+ define_method(m) do |opts|
83
+ "\t<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
84
+ end
85
+ end
86
+
87
+ def td(opts)
88
+ tdtype = opts[:th] ? 'th' : 'td'
89
+ "\t\t<#{tdtype}#{pba(opts)}>#{opts[:text]}</#{tdtype}>\n"
90
+ end
91
+
92
+ def tr_open(opts)
93
+ "\t<tr#{pba(opts)}>\n"
94
+ end
95
+
96
+ def tr_close(opts)
97
+ "\t</tr>\n"
98
+ end
99
+
100
+ def table_open(opts)
101
+ "<table#{pba(opts)}>\n"
102
+ end
103
+
104
+ def table_close(opts)
105
+ "</table>\n"
106
+ end
107
+
108
+ def bc_open(opts)
109
+ opts[:block] = true
110
+ "<pre#{pba(opts)}>"
111
+ end
112
+
113
+ def bc_close(opts)
114
+ "</pre>\n"
115
+ end
116
+
117
+ def bq_open(opts)
118
+ opts[:block] = true
119
+ cite = opts[:cite] ? " cite=\"#{ opts[:cite] }\"" : ''
120
+ "<blockquote#{cite}#{pba(opts)}>\n"
121
+ end
122
+
123
+ def bq_close(opts)
124
+ "</blockquote>\n"
125
+ end
126
+
127
+ LINK_TEXT_WITH_TITLE_RE = /
128
+ ([^"]+?) # $text
129
+ \s?
130
+ \(([^)]+?)\) # $title
131
+ $
132
+ /x
133
+ def link(opts)
134
+ if opts[:name] =~ LINK_TEXT_WITH_TITLE_RE
135
+ md = LINK_TEXT_WITH_TITLE_RE.match(opts[:name])
136
+ opts[:name] = md[1]
137
+ opts[:title] = md[2]
138
+ end
139
+ "<a href=\"#{opts[:href].gsub(/&/, '&#38;')}\"#{pba(opts)}>#{opts[:name]}</a>"
140
+ end
141
+
142
+ def image(opts)
143
+ opts.delete(:align)
144
+ opts[:alt] = opts[:title]
145
+ img = "<img src=\"#{urlesc opts[:src]}\"#{pba(opts)} alt=\"#{opts[:alt]}\" />"
146
+ img = "<a href=\"#{urlesc opts[:href]}\">#{img}</a>" if opts[:href]
147
+ img
148
+ end
149
+
150
+ def footno(opts)
151
+ opts[:id] ||= opts[:text]
152
+ %Q{<sup class="footnote"><a href=\"#fn#{opts[:id]}\">#{opts[:text]}</a></sup>}
153
+ end
154
+
155
+ def fn(opts)
156
+ no = opts[:id]
157
+ opts[:id] = "fn#{no}"
158
+ opts[:class] = ["footnote", opts[:class]].compact.join(" ")
159
+ "<p#{pba(opts)}><sup>#{no}</sup> #{opts[:text]}</p>\n"
160
+ end
161
+
162
+ def snip(opts)
163
+ "<pre#{pba(opts)}><code>#{opts[:text]}</code></pre>\n"
164
+ end
165
+
166
+ def quote1(opts)
167
+ "&#8216;#{opts[:text]}&#8217;"
168
+ end
169
+
170
+ def quote2(opts)
171
+ "&#8220;#{opts[:text]}&#8221;"
172
+ end
173
+
174
+ def ellipsis(opts)
175
+ "#{opts[:text]}&#8230;"
176
+ end
177
+
178
+ def emdash(opts)
179
+ "&#8212;"
180
+ end
181
+
182
+ def endash(opts)
183
+ " &#8211; "
184
+ end
185
+
186
+ def arrow(opts)
187
+ "&#8594;"
188
+ end
189
+
190
+ def dim(opts)
191
+ space = opts[:space] ? " " : ''
192
+ "#{opts[:text]}#{space}&#215;#{space}"
193
+ end
194
+
195
+ def trademark(opts)
196
+ "&#8482;"
197
+ end
198
+
199
+ def registered(opts)
200
+ "&#174;"
201
+ end
202
+
203
+ def copyright(opts)
204
+ "&#169;"
205
+ end
206
+
207
+ def entity(opts)
208
+ "&#{opts[:text]};"
209
+ end
210
+
211
+ def urlesc(txt)
212
+ txt.gsub(/&/, '&amp;')
213
+ end
214
+
215
+ def amp(opts)
216
+ "&amp;"
217
+ end
218
+
219
+ def gt(opts)
220
+ "&gt;"
221
+ end
222
+
223
+ def lt(opts)
224
+ "&lt;"
225
+ end
226
+
227
+ def br(opts)
228
+ if hard_breaks == false
229
+ "\n"
230
+ else
231
+ "<br />\n"
232
+ end
233
+ end
234
+
235
+ def quot(opts)
236
+ "&quot;"
237
+ end
238
+
239
+ def squot(opts)
240
+ "&#8217;"
241
+ end
242
+
243
+ def html(opts)
244
+ "#{opts[:text]}\n"
245
+ end
246
+
247
+ def html_block(opts)
248
+ inline_html(:text => "#{opts[:indent_before_start]}#{opts[:start_tag]}#{opts[:indent_after_start]}") +
249
+ "#{opts[:text]}" +
250
+ inline_html(:text => "#{opts[:indent_before_end]}#{opts[:end_tag]}#{opts[:indent_after_end]}")
251
+ end
252
+
253
+ def notextile(opts)
254
+ if filter_html
255
+ html_esc(opts[:text], :html_escape_preformatted)
256
+ else
257
+ opts[:text]
258
+ end
259
+ end
260
+
261
+ def inline_html(opts)
262
+ if filter_html
263
+ html_esc(opts[:text], :html_escape_preformatted)
264
+ else
265
+ "#{opts[:text]}" # nil-safe
266
+ end
267
+ end
268
+
269
+ def ignored_line(opts)
270
+ opts[:text] + "\n"
271
+ end
272
+
273
+ def before_transform(text)
274
+ clean_html(text) if sanitize_html
275
+ end
276
+
277
+ # HTML cleansing stuff
278
+ BASIC_TAGS = {
279
+ 'a' => ['href', 'title'],
280
+ 'img' => ['src', 'alt', 'title'],
281
+ 'br' => [],
282
+ 'i' => nil,
283
+ 'u' => nil,
284
+ 'b' => nil,
285
+ 'pre' => nil,
286
+ 'kbd' => nil,
287
+ 'code' => ['lang'],
288
+ 'cite' => nil,
289
+ 'strong' => nil,
290
+ 'em' => nil,
291
+ 'ins' => nil,
292
+ 'sup' => nil,
293
+ 'sub' => nil,
294
+ 'del' => nil,
295
+ 'table' => nil,
296
+ 'tr' => nil,
297
+ 'td' => ['colspan', 'rowspan'],
298
+ 'th' => nil,
299
+ 'ol' => ['start'],
300
+ 'ul' => nil,
301
+ 'li' => nil,
302
+ 'p' => nil,
303
+ 'h1' => nil,
304
+ 'h2' => nil,
305
+ 'h3' => nil,
306
+ 'h4' => nil,
307
+ 'h5' => nil,
308
+ 'h6' => nil,
309
+ 'blockquote' => ['cite'],
310
+ 'notextile' => nil
311
+ }
312
+
313
+ # Clean unauthorized tags.
314
+ def clean_html( text, allowed_tags = BASIC_TAGS )
315
+ text.gsub!( /<!\[CDATA\[/, '' )
316
+ text.gsub!( /<(\/*)([A-Za-z]\w*)([^>]*?)(\s?\/?)>/ ) do |m|
317
+ raw = $~
318
+ tag = raw[2].downcase
319
+ if allowed_tags.has_key? tag
320
+ pcs = [tag]
321
+ allowed_tags[tag].each do |prop|
322
+ ['"', "'", ''].each do |q|
323
+ q2 = ( q != '' ? q : '\s' )
324
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
325
+ attrv = $1
326
+ next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
327
+ pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
328
+ break
329
+ end
330
+ end
331
+ end if allowed_tags[tag]
332
+ "<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
333
+ else # Unauthorized tag
334
+ if block_given?
335
+ yield m
336
+ else
337
+ ''
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,227 @@
1
+ require 'yaml'
2
+
3
+ module RedCloth::Formatters::LATEX
4
+ include RedCloth::Formatters::Base
5
+
6
+ ENTITIES = YAML::load(File.read(File.dirname(__FILE__)+'/latex_entities.yml'))
7
+
8
+ def escape(text)
9
+ latex_esc(text)
10
+ end
11
+
12
+ def escape_pre(text)
13
+ text
14
+ end
15
+
16
+ # headers
17
+ { :h1 => 'section*',
18
+ :h2 => 'subsection*',
19
+ :h3 => 'subsubsection*',
20
+ :h4 => 'textbf',
21
+ :h5 => 'textbf',
22
+ :h6 => 'textbf',
23
+ }.each do |m,tag|
24
+ define_method(m) do |opts|
25
+ "\\#{tag}{#{opts[:text]}}\n\n"
26
+ end
27
+ end
28
+
29
+ # commands
30
+ { :strong => 'textbf',
31
+ :em => 'emph',
32
+ :i => 'emph',
33
+ :b => 'textbf',
34
+ :ins => 'underline',
35
+ :del => 'sout',
36
+ :acronym => 'MakeUppercase',
37
+ :caps => 'MakeUppercase',
38
+ }.each do |m,tag|
39
+ define_method(m) do |opts|
40
+ "\\#{tag}{#{opts[:text]}}"
41
+ end
42
+ end
43
+
44
+ { :sup => '\ensuremath{^\textrm{#1}}',
45
+ :sub => '\ensuremath{_\textrm{#1}}',
46
+ }.each do |m, expr|
47
+ define_method(m) do |opts|
48
+ expr.sub('#1', opts[:text])
49
+ end
50
+ end
51
+
52
+ # environments
53
+ { :pre => 'verbatim',
54
+ :code => 'verbatim',
55
+ :cite => 'quote',
56
+ }.each do |m, env|
57
+ define_method(m) do |opts|
58
+ begin_chunk(env) + opts[:text] + end_chunk(env)
59
+ end
60
+ end
61
+
62
+ # ignore (or find a good solution later)
63
+ [ :span,
64
+ :div,
65
+ ].each do |m|
66
+ define_method(m) do |opts|
67
+ opts[:text].to_s
68
+ end
69
+ end
70
+
71
+ def del_phrase(opts)
72
+ " #{del(opts)}"
73
+ end
74
+
75
+ { :ol => 'enumerate',
76
+ :ul => 'itemize',
77
+ }.each do |m, env|
78
+ define_method("#{m}_open") do |opts|
79
+ opts[:block] = true
80
+ "\\begin{#{env}}\n"
81
+ end
82
+ define_method("#{m}_close") do |opts|
83
+ "#{li_close}\\end{#{env}}\n\n"
84
+ end
85
+ end
86
+
87
+ def li_open(opts)
88
+ "#{li_close unless opts.delete(:first)}\t\\item #{opts[:text]}"
89
+ end
90
+
91
+ def li_close(opts=nil)
92
+ "\n"
93
+ end
94
+
95
+ def p(opts)
96
+ opts[:text] + "\n\n"
97
+ end
98
+
99
+ def td(opts)
100
+ "\t\t\t#{opts[:text]} &\n"
101
+ end
102
+
103
+ def tr_open(opts)
104
+ "\t\t"
105
+ end
106
+
107
+ def tr_close(opts)
108
+ "\t\t\\\\\n"
109
+ end
110
+
111
+ # FIXME: we need to know the column count before opening tabular context.
112
+ def table_open(opts)
113
+ "\\begin{align*}\n"
114
+ end
115
+
116
+ def table_close(opts)
117
+ "\t\\end{align*}\n"
118
+ end
119
+
120
+ def bc_open(opts)
121
+ opts[:block] = true
122
+ begin_chunk("verbatim") + "\n"
123
+ end
124
+
125
+ def bc_close(opts)
126
+ end_chunk("verbatim") + "\n"
127
+ end
128
+
129
+ def bq_open(opts)
130
+ opts[:block] = true
131
+ "\\begin{quotation}\n"
132
+ end
133
+
134
+ def bq_close(opts)
135
+ "\\end{quotation}\n\n"
136
+ end
137
+
138
+ def link(opts)
139
+ "\\href{#{opts[:href]}}{#{opts[:name]}}"
140
+ end
141
+
142
+ # FIXME: use includegraphics with security verification
143
+ def image(opts)
144
+ ""
145
+ end
146
+
147
+ def footno(opts)
148
+ # TODO: insert a placeholder until we know the footnote content.
149
+ # For this to work, we need some kind of post-processing...
150
+ "\\footnotemark[#{opts[:text]}]"
151
+ end
152
+
153
+ def fn(opts)
154
+ "\\footnotetext[#{opts[:id]}]{#{opts[:text]}}"
155
+ end
156
+
157
+ def snip(opts)
158
+ "\\begin{verbatim}#{opts[:text]}\\end{verbatim}"
159
+ end
160
+
161
+ def quote1(opts)
162
+ "`#{opts[:text]}'"
163
+ end
164
+
165
+ def quote2(opts)
166
+ "``#{opts[:text]}\""
167
+ end
168
+
169
+ def ellipsis(opts)
170
+ "#{opts[:text]}\\ldots{}"
171
+ end
172
+
173
+ def emdash(opts)
174
+ "---"
175
+ end
176
+
177
+ def endash(opts)
178
+ "--"
179
+ end
180
+
181
+ def arrow(opts)
182
+ "\\rightarrow{}"
183
+ end
184
+
185
+ def trademark(opts)
186
+ "\\texttrademark{}"
187
+ end
188
+
189
+ def registered(opts)
190
+ "\\textregistered{}"
191
+ end
192
+
193
+ def copyright(opts)
194
+ "\\copyright{}"
195
+ end
196
+
197
+ # TODO: what do we do with (unknown) unicode entities ?
198
+ #
199
+ def entity(opts)
200
+ text = opts[:text][0..0] == '#' ? opts[:text][1..-1] : opts[:text]
201
+ ENTITIES[text]
202
+ end
203
+
204
+ def dim(opts)
205
+ space = opts[:space] ? " " : ''
206
+ "#{opts[:text]}#{space}\\texttimes{}#{space}"
207
+ end
208
+
209
+ private
210
+
211
+ def begin_chunk(type)
212
+ chunk_counter[type] += 1
213
+ return "\\begin{#{type}}" if 1 == chunk_counter[type]
214
+ ''
215
+ end
216
+
217
+ def end_chunk(type)
218
+ chunk_counter[type] -= 1
219
+ raise RuntimeError, "Bad latex #{type} nesting detected" if chunk_counter[type] < 0 # This should never need to happen
220
+ return "\\end{#{type}}" if 0 == chunk_counter[type]
221
+ ''
222
+ end
223
+
224
+ def chunk_counter
225
+ @chunk_counter ||= Hash.new 0
226
+ end
227
+ end