RedCloth 4.2.4.pre2-x86-mingw32

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 (66) hide show
  1. data/.gitignore +25 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/CHANGELOG +232 -0
  5. data/COPYING +18 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +33 -0
  8. data/Manifest +52 -0
  9. data/README +196 -0
  10. data/Rakefile +10 -0
  11. data/bin/redcloth +28 -0
  12. data/doc/textile_reference.html +631 -0
  13. data/ext/redcloth_scan/redcloth.h +220 -0
  14. data/lib/case_sensitive_require/RedCloth.rb +6 -0
  15. data/lib/redcloth.rb +44 -0
  16. data/lib/redcloth/erb_extension.rb +27 -0
  17. data/lib/redcloth/formatters/base.rb +63 -0
  18. data/lib/redcloth/formatters/html.rb +345 -0
  19. data/lib/redcloth/formatters/latex.rb +322 -0
  20. data/lib/redcloth/formatters/latex_entities.yml +2414 -0
  21. data/lib/redcloth/textile_doc.rb +103 -0
  22. data/lib/redcloth/version.rb +34 -0
  23. data/lib/tasks/pureruby.rake +17 -0
  24. data/redcloth.gemspec +48 -0
  25. data/setup.rb +1585 -0
  26. data/spec/benchmark_spec.rb +15 -0
  27. data/spec/custom_tags_spec.rb +50 -0
  28. data/spec/erb_spec.rb +10 -0
  29. data/spec/extension_spec.rb +26 -0
  30. data/spec/fixtures/basic.yml +1028 -0
  31. data/spec/fixtures/code.yml +257 -0
  32. data/spec/fixtures/definitions.yml +82 -0
  33. data/spec/fixtures/extra_whitespace.yml +64 -0
  34. data/spec/fixtures/filter_html.yml +177 -0
  35. data/spec/fixtures/filter_pba.yml +20 -0
  36. data/spec/fixtures/html.yml +340 -0
  37. data/spec/fixtures/images.yml +279 -0
  38. data/spec/fixtures/instiki.yml +38 -0
  39. data/spec/fixtures/links.yml +291 -0
  40. data/spec/fixtures/lists.yml +462 -0
  41. data/spec/fixtures/poignant.yml +89 -0
  42. data/spec/fixtures/sanitize_html.yml +42 -0
  43. data/spec/fixtures/table.yml +434 -0
  44. data/spec/fixtures/textism.yml +509 -0
  45. data/spec/fixtures/threshold.yml +762 -0
  46. data/spec/formatters/class_filtered_html_spec.rb +7 -0
  47. data/spec/formatters/filtered_html_spec.rb +7 -0
  48. data/spec/formatters/html_no_breaks_spec.rb +9 -0
  49. data/spec/formatters/html_spec.rb +13 -0
  50. data/spec/formatters/id_filtered_html_spec.rb +7 -0
  51. data/spec/formatters/latex_spec.rb +13 -0
  52. data/spec/formatters/lite_mode_html_spec.rb +7 -0
  53. data/spec/formatters/no_span_caps_html_spec.rb +7 -0
  54. data/spec/formatters/sanitized_html_spec.rb +7 -0
  55. data/spec/formatters/style_filtered_html_spec.rb +7 -0
  56. data/spec/parser_spec.rb +95 -0
  57. data/spec/spec_helper.rb +36 -0
  58. data/tasks/compile.rake +47 -0
  59. data/tasks/gems.rake +38 -0
  60. data/tasks/ragel_extension_task.rb +127 -0
  61. data/tasks/release.rake +15 -0
  62. data/tasks/rspec.rake +11 -0
  63. data/tasks/rvm.rake +43 -0
  64. data/test/ragel_profiler.rb +73 -0
  65. data/test/validate_fixtures.rb +74 -0
  66. metadata +218 -0
@@ -0,0 +1,220 @@
1
+ #ifndef redcloth_h
2
+ #define redcloth_h
3
+
4
+ #ifndef RARRAY_LEN
5
+ #define RARRAY_LEN(arr) RARRAY(arr)->len
6
+ #define RSTRING_LEN(str) RSTRING(str)->len
7
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
8
+ #endif
9
+
10
+
11
+ // Different string conversions for ruby 1.8 and ruby 1.9. For 1.9,
12
+ // we need to set the encoding of the string.
13
+
14
+ // For Ruby 1.9
15
+ #ifdef HAVE_RUBY_ENCODING_H
16
+ #include "ruby/encoding.h"
17
+ #define STR_NEW(p,n) rb_enc_str_new((p),(n),rb_enc_from_index(ENCODING_GET(self)))
18
+ #define STR_NEW2(p) rb_enc_str_new((p),strlen(p),rb_enc_from_index(ENCODING_GET(self)))
19
+
20
+ // For Ruby 1.8
21
+ #else
22
+ #define STR_NEW(p,n) rb_str_new((p),(n))
23
+ #define STR_NEW2(p) rb_str_new2((p))
24
+
25
+ #endif
26
+
27
+
28
+ /* variable defs */
29
+ #ifndef redcloth_scan_c
30
+ extern VALUE super_ParseError, mRedCloth, super_RedCloth;
31
+ extern int SYM_escape_preformatted;
32
+ #endif
33
+
34
+ /* function defs */
35
+ void rb_str_cat_escaped(VALUE self, VALUE str, char *ts, char *te);
36
+ void rb_str_cat_escaped_for_preformatted(VALUE self, VALUE str, char *ts, char *te);
37
+ VALUE redcloth_inline(VALUE, char *, char *, VALUE);
38
+ VALUE redcloth_inline2(VALUE, VALUE, VALUE);
39
+ VALUE redcloth_attribute_parser(int, VALUE, char *, char *);
40
+ VALUE redcloth_attributes(VALUE, VALUE);
41
+ VALUE redcloth_link_attributes(VALUE, VALUE);
42
+ VALUE red_parse_title(VALUE, VALUE, VALUE);
43
+ VALUE redcloth_transform(VALUE, char *, char *, VALUE);
44
+ VALUE redcloth_transform2(VALUE, VALUE);
45
+ void red_inc(VALUE, VALUE);
46
+ VALUE red_block(VALUE, VALUE, VALUE, VALUE);
47
+ VALUE red_blockcode(VALUE, VALUE, VALUE);
48
+ VALUE red_pass(VALUE, VALUE, VALUE, ID, VALUE);
49
+ VALUE red_pass_code(VALUE, VALUE, VALUE, ID);
50
+
51
+ /* parser macros */
52
+ #define CLEAR_REGS() regs = rb_hash_new(); attr_regs = rb_hash_new();
53
+ #define RESET_REG() reg = NULL
54
+ #define MARK() reg = p;
55
+ #define MARK_B() bck = p;
56
+ #define MARK_ATTR() attr_reg = p;
57
+ #define CAT(H) rb_str_cat(H, ts, te-ts)
58
+ #define CLEAR(H) H = STR_NEW2("")
59
+ #define RSTRIP_BANG(H) rb_funcall(H, rb_intern("rstrip!"), 0)
60
+ #define SET_PLAIN_BLOCK(T) plain_block = STR_NEW2(T)
61
+ #define RESET_TYPE(T) rb_hash_aset(regs, ID2SYM(rb_intern("type")), plain_block)
62
+ #define INLINE(H, T) rb_str_append(H, rb_funcall(self, rb_intern(T), 1, regs))
63
+ #define DONE(H) rb_str_append(html, H); CLEAR(H); CLEAR_REGS()
64
+ #define PASS(H, A, T) rb_str_append(H, red_pass(self, regs, ID2SYM(rb_intern(A)), rb_intern(T), refs))
65
+ #define PARSE_ATTR(A) red_parse_attr(self, regs, ID2SYM(rb_intern(A)))
66
+ #define PARSE_LINK_ATTR(A) red_parse_link_attr(self, regs, ID2SYM(rb_intern(A)))
67
+ #define PARSE_IMAGE_ATTR(A) red_parse_image_attr(self, regs, ID2SYM(rb_intern(A)))
68
+ #define PASS_CODE(H, A, T) rb_str_append(H, red_pass_code(self, regs, ID2SYM(rb_intern(A)), rb_intern(T)))
69
+ #define ADD_BLOCK() \
70
+ rb_str_append(html, red_block(self, regs, block, refs)); \
71
+ extend = Qnil; \
72
+ CLEAR(block); \
73
+ CLEAR_REGS()
74
+ #define ADD_EXTENDED_BLOCK() rb_str_append(html, red_block(self, regs, block, refs)); CLEAR(block);
75
+ #define END_EXTENDED() extend = Qnil; CLEAR_REGS();
76
+ #define ADD_BLOCKCODE() rb_str_append(html, red_blockcode(self, regs, block)); CLEAR(block); CLEAR_REGS()
77
+ #define ADD_EXTENDED_BLOCKCODE() rb_str_append(html, red_blockcode(self, regs, block)); CLEAR(block);
78
+ #define ASET(T, V) rb_hash_aset(regs, ID2SYM(rb_intern(T)), STR_NEW2(V));
79
+ #define ATTR_SET(T, V) rb_hash_aset(attr_regs, ID2SYM(rb_intern(T)), STR_NEW2(V));
80
+ #define ATTR_INC(T) red_inc(attr_regs, ID2SYM(rb_intern(T)));
81
+ #define INC(N) N++;
82
+ #define SET_ATTRIBUTES() \
83
+ SET_ATTRIBUTE("class_buf", "class"); \
84
+ SET_ATTRIBUTE("id_buf", "id"); \
85
+ SET_ATTRIBUTE("lang_buf", "lang"); \
86
+ SET_ATTRIBUTE("style_buf", "style"); \
87
+ rb_funcall(regs, rb_intern("merge!"), 1, attr_regs); \
88
+ attr_regs = rb_hash_new();
89
+ #define SET_ATTRIBUTE(B, A) \
90
+ if (rb_hash_aref(regs, ID2SYM(rb_intern(B))) != Qnil) rb_hash_aset(regs, ID2SYM(rb_intern(A)), rb_hash_aref(regs, ID2SYM(rb_intern(B))));
91
+ #define TRANSFORM(T) \
92
+ if (p > reg && reg >= ts) { \
93
+ VALUE str = redcloth_transform(self, reg, p, refs); \
94
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), str); \
95
+ /*printf("TRANSFORM(" T ") '%s' (p:'%s' reg:'%s')\n", RSTRING_PTR(str), p, reg);*/ \
96
+ } else { \
97
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), Qnil); \
98
+ }
99
+ #define STORE(T) \
100
+ if (p > reg && reg >= ts) { \
101
+ VALUE str = STR_NEW(reg, p-reg); \
102
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), str); \
103
+ /*printf("STORE(" T ") '%s' (p:'%s' reg:'%s')\n", RSTRING_PTR(str), p, reg);*/ \
104
+ } else { \
105
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), Qnil); \
106
+ }
107
+ #define STORE_B(T) \
108
+ if (p > bck && bck >= ts) { \
109
+ VALUE str = STR_NEW(bck, p-bck); \
110
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), str); \
111
+ /*printf("STORE_B(" T ") '%s' (p:'%s' reg:'%s')\n", RSTRING_PTR(str), p, reg);*/ \
112
+ } else { \
113
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), Qnil); \
114
+ }
115
+ #define STORE_ATTR(T) \
116
+ if (p > attr_reg && attr_reg >= ts) { \
117
+ VALUE str = STR_NEW(attr_reg, p-attr_reg); \
118
+ rb_hash_aset(attr_regs, ID2SYM(rb_intern(T)), str); \
119
+ /*printf("STORE_B(" T ") '%s' (p:'%s' reg:'%s')\n", RSTRING_PTR(str), p, reg);*/ \
120
+ } else { \
121
+ rb_hash_aset(attr_regs, ID2SYM(rb_intern(T)), Qnil); \
122
+ }
123
+
124
+ #define STORE_URL(T) \
125
+ if (p > reg && reg >= ts) { \
126
+ char punct = 1; \
127
+ while (p > reg && punct == 1) { \
128
+ switch (*(p - 1)) { \
129
+ case ')': \
130
+ { /*needed to keep inside chars scoped for less memory usage*/\
131
+ char *temp_p = p - 1; \
132
+ signed char level = -1; \
133
+ while (temp_p > reg) { \
134
+ switch(*(temp_p - 1)) { \
135
+ case '(': ++level; break; \
136
+ case ')': --level; break; \
137
+ } \
138
+ --temp_p; \
139
+ } \
140
+ if (level == 0) { punct = 0; } else { --p; } \
141
+ } \
142
+ break; \
143
+ case '!': case '"': case '#': case '$': case '%': case ']': case '[': case '&': case '\'': \
144
+ case '*': case '+': case ',': case '-': case '.': case '(': case ':': \
145
+ case ';': case '=': case '?': case '@': case '\\': case '^': case '_': \
146
+ case '`': case '|': case '~': p--; break; \
147
+ default: punct = 0; \
148
+ } \
149
+ } \
150
+ te = p; \
151
+ } \
152
+ STORE(T); \
153
+ if ( !NIL_P(refs) && rb_funcall(refs, rb_intern("has_key?"), 1, rb_hash_aref(regs, ID2SYM(rb_intern(T)))) ) { \
154
+ rb_hash_aset(regs, ID2SYM(rb_intern(T)), rb_hash_aref(refs, rb_hash_aref(regs, ID2SYM(rb_intern(T))))); \
155
+ }
156
+ #define STORE_LINK_ALIAS() \
157
+ rb_hash_aset(refs_found, rb_hash_aref(regs, ID2SYM(rb_intern("text"))), rb_hash_aref(regs, ID2SYM(rb_intern("href"))))
158
+ #define CLEAR_LIST() list_layout = rb_ary_new()
159
+ #define SET_LIST_TYPE(T) list_type = T;
160
+ #define NEST() nest ++;
161
+ #define RESET_NEST() nest = 0;
162
+ #define LIST_LAYOUT() \
163
+ int aint = 0; \
164
+ VALUE aval = rb_ary_entry(list_index, nest-1); \
165
+ if (aval != Qnil) aint = NUM2INT(aval); \
166
+ if (strcmp(list_type, "ol") == 0 && nest > 0) \
167
+ { \
168
+ rb_ary_store(list_index, nest-1, INT2NUM(aint + 1)); \
169
+ } \
170
+ if (nest > RARRAY_LEN(list_layout)) \
171
+ { \
172
+ SET_ATTRIBUTES(); \
173
+ sprintf(listm, "%s_open", list_type); \
174
+ if (!NIL_P(rb_hash_aref(regs, ID2SYM(rb_intern("list_continue"))))) \
175
+ { \
176
+ rb_hash_aset(regs, ID2SYM(rb_intern("list_continue")), Qnil); \
177
+ rb_hash_aset(regs, ID2SYM(rb_intern("start")), rb_ary_entry(list_index, nest-1)); \
178
+ } \
179
+ else \
180
+ { \
181
+ VALUE start = rb_hash_aref(regs, ID2SYM(rb_intern("start"))); \
182
+ if (NIL_P(start) ) \
183
+ { \
184
+ rb_ary_store(list_index, nest-1, INT2NUM(1)); \
185
+ } \
186
+ else \
187
+ { \
188
+ VALUE start_num = rb_funcall(start,rb_intern("to_i"),0); \
189
+ rb_ary_store(list_index, nest-1, start_num); \
190
+ } \
191
+ } \
192
+ rb_hash_aset(regs, ID2SYM(rb_intern("nest")), INT2NUM(nest)); \
193
+ rb_str_append(html, rb_funcall(self, rb_intern(listm), 1, regs)); \
194
+ rb_ary_store(list_layout, nest-1, STR_NEW2(list_type)); \
195
+ CLEAR_REGS(); \
196
+ ASET("first", "true"); \
197
+ } \
198
+ LIST_CLOSE(); \
199
+ if (nest != 0) LIST_ITEM_CLOSE(); \
200
+ CLEAR_REGS(); \
201
+ rb_hash_aset(regs, ID2SYM(rb_intern("nest")), INT2NUM(RARRAY_LEN(list_layout))); \
202
+ ASET("type", "li_open");
203
+ #define LIST_ITEM_CLOSE() \
204
+ if ( rb_hash_aref(regs, ID2SYM(rb_intern("first"))) == Qnil ) \
205
+ rb_str_append(html, rb_funcall(self, rb_intern("li_close"), 1, regs));
206
+ #define LIST_CLOSE() \
207
+ while (nest < RARRAY_LEN(list_layout)) \
208
+ { \
209
+ rb_hash_aset(regs, ID2SYM(rb_intern("nest")), INT2NUM(RARRAY_LEN(list_layout))); \
210
+ VALUE end_list = rb_ary_pop(list_layout); \
211
+ if (!NIL_P(end_list)) \
212
+ { \
213
+ StringValue(end_list); \
214
+ sprintf(listm, "%s_close", RSTRING_PTR(end_list)); \
215
+ LIST_ITEM_CLOSE(); \
216
+ rb_str_append(html, rb_funcall(self, rb_intern(listm), 1, regs)); \
217
+ } \
218
+ }
219
+
220
+ #endif
@@ -0,0 +1,6 @@
1
+ # A workaround to make Rails 2.1 gem dependency easier on case-sensitive filesystems.
2
+ # Since the gem name is RedCloth and the file is redcloth.rb, config.gem 'RedCloth' doesn't
3
+ # work. You'd have to use config.gem 'RedCloth', :lib => 'redcloth', and that's not
4
+ # immediately obvious. This file remedies that.
5
+ #
6
+ require File.join(File.dirname(__FILE__), '..', 'redcloth')
@@ -0,0 +1,44 @@
1
+ # If this is a frozen gem in Rails 2.1 and RedCloth 3.x was already
2
+ # loaded by Rails' ActionView::Helpers::TextHelper, the user will get
3
+ # "redcloth_scan.bundle: Class is not a module (TypeError)"
4
+ # This hack is to work around that Rails loading problem. The problem
5
+ # appears to be fixed in Edge Rails [51e4106].
6
+ Object.send(:remove_const, :RedCloth) if Object.const_defined?(:RedCloth) && RedCloth.is_a?(Class)
7
+
8
+ require 'rbconfig'
9
+ begin
10
+ prefix = Config::CONFIG['arch'] =~ /mswin|mingw/ ? "#{Config::CONFIG['MAJOR']}.#{Config::CONFIG['MINOR']}/" : ''
11
+ lib = "#{prefix}redcloth_scan"
12
+ require lib
13
+ rescue LoadError => e
14
+ e.message << %{\nCouldn't load #{lib}\nThe $LOAD_PATH was:\n#{$LOAD_PATH.join("\n")}}
15
+ raise e
16
+ end
17
+
18
+ require 'redcloth/version'
19
+ require 'redcloth/textile_doc'
20
+ require 'redcloth/formatters/base'
21
+ require 'redcloth/formatters/html'
22
+ require 'redcloth/formatters/latex'
23
+
24
+ module RedCloth
25
+
26
+ # A convenience method for creating a new TextileDoc. See
27
+ # RedCloth::TextileDoc.
28
+ def self.new( *args, &block )
29
+ RedCloth::TextileDoc.new( *args, &block )
30
+ end
31
+
32
+ # Include extension modules (if any) in TextileDoc.
33
+ def self.include(*args)
34
+ RedCloth::TextileDoc.send(:include, *args)
35
+ end
36
+
37
+ end
38
+
39
+ begin
40
+ require 'erb'
41
+ require 'redcloth/erb_extension'
42
+ include ERB::Util
43
+ rescue LoadError
44
+ end
@@ -0,0 +1,27 @@
1
+ class ERB
2
+ module Util
3
+
4
+ #
5
+ # A utility method for transforming Textile in _s_ to HTML.
6
+ #
7
+ # require "erb"
8
+ # include ERB::Util
9
+ #
10
+ # puts textilize("Isn't ERB *great*?")
11
+ #
12
+ # _Generates_
13
+ #
14
+ # <p>Isn&#8217;t <span class="caps">ERB</span> <strong>great</strong>?</p>
15
+ #
16
+ def textilize( s )
17
+ if s && s.respond_to?(:to_s)
18
+ RedCloth.new( s.to_s ).to_html
19
+ end
20
+ end
21
+
22
+ alias t textilize
23
+ module_function :t
24
+ module_function :textilize
25
+
26
+ end
27
+ end
@@ -0,0 +1,63 @@
1
+ module RedCloth::Formatters
2
+ module Base
3
+
4
+ def ignore(opts)
5
+ opts[:text]
6
+ end
7
+ alias_method :notextile, :ignore
8
+
9
+ def redcloth_version(opts)
10
+ p(:text => "#{opts[:prefix]}#{RedCloth::VERSION}")
11
+ end
12
+
13
+ def inline_redcloth_version(opts)
14
+ RedCloth::VERSION::STRING
15
+ end
16
+
17
+ [:del_phrase, :sup_phrase, :sub_phrase, :span_phrase].each do |phrase_method|
18
+ method = phrase_method.to_s.split('_')[0]
19
+ define_method(phrase_method) do |opts|
20
+ "#{opts[:beginning_space]}#{self.send(method, opts)}"
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def pba(opts)
27
+ opts.delete(:style) if filter_styles
28
+ opts.delete(:class) if filter_classes
29
+ opts.delete(:id) if filter_ids
30
+
31
+ atts = ''
32
+ opts[:"text-align"] = opts.delete(:align)
33
+ opts[:style] += ';' if opts[:style] && (opts[:style][-1..-1] != ';')
34
+ [:float, :"text-align", :"vertical-align"].each do |a|
35
+ opts[:style] = "#{a}:#{opts[a]};#{opts[:style]}" if opts[a]
36
+ end
37
+ [:"padding-right", :"padding-left"].each do |a|
38
+ opts[:style] = "#{a}:#{opts[a]}em;#{opts[:style]}" if opts[a]
39
+ end
40
+ [:style, :class, :lang, :id, :colspan, :rowspan, :title, :start, :align].each do |a|
41
+ atts << " #{a}=\"#{ html_esc(opts[a].to_s, :html_escape_attributes) }\"" if opts[a]
42
+ end
43
+ atts
44
+ end
45
+
46
+ def method_missing(method, opts)
47
+ opts[:text] || ""
48
+ end
49
+
50
+ def before_transform(text)
51
+
52
+ end
53
+
54
+ def after_transform(text)
55
+
56
+ end
57
+
58
+ def formatter_methods
59
+ singleton_methods.map! {|method| method.to_sym }
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,345 @@
1
+ module RedCloth::Formatters::HTML
2
+ include RedCloth::Formatters::Base
3
+
4
+ [:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
5
+ define_method(m) do |opts|
6
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
7
+ end
8
+ end
9
+
10
+ [:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
11
+ define_method(m) do |opts|
12
+ opts[:block] = true
13
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>"
14
+ end
15
+ end
16
+
17
+ def hr(opts)
18
+ "<hr />\n"
19
+ end
20
+
21
+ def acronym(opts)
22
+ opts[:block] = true
23
+ "<acronym#{pba(opts)}>#{caps(:text => opts[:text])}</acronym>"
24
+ end
25
+
26
+ def caps(opts)
27
+ if no_span_caps
28
+ opts[:text]
29
+ else
30
+ opts[:class] = 'caps'
31
+ span(opts)
32
+ end
33
+ end
34
+
35
+ def del(opts)
36
+ opts[:block] = true
37
+ "<del#{pba(opts)}>#{opts[:text]}</del>"
38
+ end
39
+
40
+ [:ol, :ul].each do |m|
41
+ define_method("#{m}_open") do |opts|
42
+ opts[:block] = true
43
+ "#{"\n" if opts[:nest] > 1}#{"\t" * (opts[:nest] - 1)}<#{m}#{pba(opts)}>\n"
44
+ end
45
+ define_method("#{m}_close") do |opts|
46
+ "#{"\t" * (opts[:nest] - 1)}</#{m}>#{"\n" if opts[:nest] <= 1}"
47
+ end
48
+ end
49
+
50
+ def li_open(opts)
51
+ "#{"\t" * opts[:nest]}<li#{pba(opts)}>#{opts[:text]}"
52
+ end
53
+
54
+ def li_close(opts=nil)
55
+ "</li>\n"
56
+ end
57
+
58
+ def dl_open(opts)
59
+ opts[:block] = true
60
+ "<dl#{pba(opts)}>\n"
61
+ end
62
+
63
+ def dl_close(opts=nil)
64
+ "</dl>\n"
65
+ end
66
+
67
+ [:dt, :dd].each do |m|
68
+ define_method(m) do |opts|
69
+ "\t<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
70
+ end
71
+ end
72
+
73
+ def td(opts)
74
+ tdtype = opts[:th] ? 'th' : 'td'
75
+ "\t\t<#{tdtype}#{pba(opts)}>#{opts[:text]}</#{tdtype}>\n"
76
+ end
77
+
78
+ def tr_open(opts)
79
+ "\t<tr#{pba(opts)}>\n"
80
+ end
81
+
82
+ def tr_close(opts)
83
+ "\t</tr>\n"
84
+ end
85
+
86
+ def table_open(opts)
87
+ "<table#{pba(opts)}>\n"
88
+ end
89
+
90
+ def table_close(opts)
91
+ "</table>\n"
92
+ end
93
+
94
+ def bc_open(opts)
95
+ opts[:block] = true
96
+ "<pre#{pba(opts)}>"
97
+ end
98
+
99
+ def bc_close(opts)
100
+ "</pre>\n"
101
+ end
102
+
103
+ def bq_open(opts)
104
+ opts[:block] = true
105
+ cite = opts[:cite] ? " cite=\"#{ escape_attribute opts[:cite] }\"" : ''
106
+ "<blockquote#{cite}#{pba(opts)}>\n"
107
+ end
108
+
109
+ def bq_close(opts)
110
+ "</blockquote>\n"
111
+ end
112
+
113
+ def link(opts)
114
+ "<a href=\"#{escape_attribute opts[:href]}\"#{pba(opts)}>#{opts[:name]}</a>"
115
+ end
116
+
117
+ def image(opts)
118
+ opts.delete(:align)
119
+ opts[:alt] = opts[:title]
120
+ img = "<img src=\"#{escape_attribute opts[:src]}\"#{pba(opts)} alt=\"#{escape_attribute opts[:alt].to_s}\" />"
121
+ img = "<a href=\"#{escape_attribute opts[:href]}\">#{img}</a>" if opts[:href]
122
+ img
123
+ end
124
+
125
+ def footno(opts)
126
+ opts[:id] ||= opts[:text]
127
+ %Q{<sup class="footnote" id=\"fnr#{opts[:id]}\"><a href=\"#fn#{opts[:id]}\">#{opts[:text]}</a></sup>}
128
+ end
129
+
130
+ def fn(opts)
131
+ no = opts[:id]
132
+ opts[:id] = "fn#{no}"
133
+ opts[:class] = ["footnote", opts[:class]].compact.join(" ")
134
+ "<p#{pba(opts)}><a href=\"#fnr#{no}\"><sup>#{no}</sup></a> #{opts[:text]}</p>\n"
135
+ end
136
+
137
+ def snip(opts)
138
+ "<pre#{pba(opts)}><code>#{opts[:text]}</code></pre>\n"
139
+ end
140
+
141
+ def quote1(opts)
142
+ "&#8216;#{opts[:text]}&#8217;"
143
+ end
144
+
145
+ def quote2(opts)
146
+ "&#8220;#{opts[:text]}&#8221;"
147
+ end
148
+
149
+ def multi_paragraph_quote(opts)
150
+ "&#8220;#{opts[:text]}"
151
+ end
152
+
153
+ def ellipsis(opts)
154
+ "#{opts[:text]}&#8230;"
155
+ end
156
+
157
+ def emdash(opts)
158
+ "&#8212;"
159
+ end
160
+
161
+ def endash(opts)
162
+ " &#8211; "
163
+ end
164
+
165
+ def arrow(opts)
166
+ "&#8594;"
167
+ end
168
+
169
+ def dim(opts)
170
+ opts[:text].gsub!('x', '&#215;')
171
+ opts[:text].gsub!("'", '&#8242;')
172
+ opts[:text].gsub!('"', '&#8243;')
173
+ opts[:text]
174
+ end
175
+
176
+ def trademark(opts)
177
+ "&#8482;"
178
+ end
179
+
180
+ def registered(opts)
181
+ "&#174;"
182
+ end
183
+
184
+ def copyright(opts)
185
+ "&#169;"
186
+ end
187
+
188
+ def entity(opts)
189
+ "&#{opts[:text]};"
190
+ end
191
+
192
+ def amp(opts)
193
+ "&amp;"
194
+ end
195
+
196
+ def gt(opts)
197
+ "&gt;"
198
+ end
199
+
200
+ def lt(opts)
201
+ "&lt;"
202
+ end
203
+
204
+ def br(opts)
205
+ if hard_breaks == false
206
+ "\n"
207
+ else
208
+ "<br />\n"
209
+ end
210
+ end
211
+
212
+ def quot(opts)
213
+ "&quot;"
214
+ end
215
+
216
+ def squot(opts)
217
+ "&#8217;"
218
+ end
219
+
220
+ def apos(opts)
221
+ "&#39;"
222
+ end
223
+
224
+ def html(opts)
225
+ "#{opts[:text]}\n"
226
+ end
227
+
228
+ def html_block(opts)
229
+ inline_html(:text => "#{opts[:indent_before_start]}#{opts[:start_tag]}#{opts[:indent_after_start]}") +
230
+ "#{opts[:text]}" +
231
+ inline_html(:text => "#{opts[:indent_before_end]}#{opts[:end_tag]}#{opts[:indent_after_end]}")
232
+ end
233
+
234
+ def notextile(opts)
235
+ if filter_html
236
+ html_esc(opts[:text], :html_escape_preformatted)
237
+ else
238
+ opts[:text]
239
+ end
240
+ end
241
+
242
+ def inline_html(opts)
243
+ if filter_html
244
+ html_esc(opts[:text], :html_escape_preformatted)
245
+ else
246
+ "#{opts[:text]}" # nil-safe
247
+ end
248
+ end
249
+
250
+ def ignored_line(opts)
251
+ opts[:text] + "\n"
252
+ end
253
+
254
+ private
255
+
256
+ # escapement for regular HTML (not in PRE tag)
257
+ def escape(text)
258
+ html_esc(text)
259
+ end
260
+
261
+ # escapement for HTML in a PRE tag
262
+ def escape_pre(text)
263
+ html_esc(text, :html_escape_preformatted)
264
+ end
265
+
266
+ # escaping for HTML attributes
267
+ def escape_attribute(text)
268
+ html_esc(text, :html_escape_attributes)
269
+ end
270
+
271
+ def after_transform(text)
272
+ text.chomp!
273
+ end
274
+
275
+
276
+ def before_transform(text)
277
+ clean_html(text) if sanitize_html
278
+ end
279
+
280
+ # HTML cleansing stuff
281
+ BASIC_TAGS = {
282
+ 'a' => ['href', 'title'],
283
+ 'img' => ['src', 'alt', 'title'],
284
+ 'br' => [],
285
+ 'i' => nil,
286
+ 'u' => nil,
287
+ 'b' => nil,
288
+ 'pre' => nil,
289
+ 'kbd' => nil,
290
+ 'code' => ['lang'],
291
+ 'cite' => nil,
292
+ 'strong' => nil,
293
+ 'em' => nil,
294
+ 'ins' => nil,
295
+ 'sup' => nil,
296
+ 'sub' => nil,
297
+ 'del' => nil,
298
+ 'table' => nil,
299
+ 'tr' => nil,
300
+ 'td' => ['colspan', 'rowspan'],
301
+ 'th' => nil,
302
+ 'ol' => ['start'],
303
+ 'ul' => nil,
304
+ 'li' => nil,
305
+ 'p' => nil,
306
+ 'h1' => nil,
307
+ 'h2' => nil,
308
+ 'h3' => nil,
309
+ 'h4' => nil,
310
+ 'h5' => nil,
311
+ 'h6' => nil,
312
+ 'blockquote' => ['cite'],
313
+ 'notextile' => nil
314
+ }
315
+
316
+ # Clean unauthorized tags.
317
+ def clean_html( text, allowed_tags = BASIC_TAGS )
318
+ text.gsub!( /<!\[CDATA\[/, '' )
319
+ text.gsub!( /<(\/*)([A-Za-z]\w*)([^>]*?)(\s?\/?)>/ ) do |m|
320
+ raw = $~
321
+ tag = raw[2].downcase
322
+ if allowed_tags.has_key? tag
323
+ pcs = [tag]
324
+ allowed_tags[tag].each do |prop|
325
+ ['"', "'", ''].each do |q|
326
+ q2 = ( q != '' ? q : '\s' )
327
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
328
+ attrv = $1
329
+ next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
330
+ pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
331
+ break
332
+ end
333
+ end
334
+ end if allowed_tags[tag]
335
+ "<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
336
+ else # Unauthorized tag
337
+ if block_given?
338
+ yield m
339
+ else
340
+ ''
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end