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.
- data/.gitignore +25 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/CHANGELOG +232 -0
- data/COPYING +18 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +33 -0
- data/Manifest +52 -0
- data/README +196 -0
- data/Rakefile +10 -0
- data/bin/redcloth +28 -0
- data/doc/textile_reference.html +631 -0
- data/ext/redcloth_scan/redcloth.h +220 -0
- data/lib/case_sensitive_require/RedCloth.rb +6 -0
- data/lib/redcloth.rb +44 -0
- data/lib/redcloth/erb_extension.rb +27 -0
- data/lib/redcloth/formatters/base.rb +63 -0
- data/lib/redcloth/formatters/html.rb +345 -0
- data/lib/redcloth/formatters/latex.rb +322 -0
- data/lib/redcloth/formatters/latex_entities.yml +2414 -0
- data/lib/redcloth/textile_doc.rb +103 -0
- data/lib/redcloth/version.rb +34 -0
- data/lib/tasks/pureruby.rake +17 -0
- data/redcloth.gemspec +48 -0
- data/setup.rb +1585 -0
- data/spec/benchmark_spec.rb +15 -0
- data/spec/custom_tags_spec.rb +50 -0
- data/spec/erb_spec.rb +10 -0
- data/spec/extension_spec.rb +26 -0
- data/spec/fixtures/basic.yml +1028 -0
- data/spec/fixtures/code.yml +257 -0
- data/spec/fixtures/definitions.yml +82 -0
- data/spec/fixtures/extra_whitespace.yml +64 -0
- data/spec/fixtures/filter_html.yml +177 -0
- data/spec/fixtures/filter_pba.yml +20 -0
- data/spec/fixtures/html.yml +340 -0
- data/spec/fixtures/images.yml +279 -0
- data/spec/fixtures/instiki.yml +38 -0
- data/spec/fixtures/links.yml +291 -0
- data/spec/fixtures/lists.yml +462 -0
- data/spec/fixtures/poignant.yml +89 -0
- data/spec/fixtures/sanitize_html.yml +42 -0
- data/spec/fixtures/table.yml +434 -0
- data/spec/fixtures/textism.yml +509 -0
- data/spec/fixtures/threshold.yml +762 -0
- data/spec/formatters/class_filtered_html_spec.rb +7 -0
- data/spec/formatters/filtered_html_spec.rb +7 -0
- data/spec/formatters/html_no_breaks_spec.rb +9 -0
- data/spec/formatters/html_spec.rb +13 -0
- data/spec/formatters/id_filtered_html_spec.rb +7 -0
- data/spec/formatters/latex_spec.rb +13 -0
- data/spec/formatters/lite_mode_html_spec.rb +7 -0
- data/spec/formatters/no_span_caps_html_spec.rb +7 -0
- data/spec/formatters/sanitized_html_spec.rb +7 -0
- data/spec/formatters/style_filtered_html_spec.rb +7 -0
- data/spec/parser_spec.rb +95 -0
- data/spec/spec_helper.rb +36 -0
- data/tasks/compile.rake +47 -0
- data/tasks/gems.rake +38 -0
- data/tasks/ragel_extension_task.rb +127 -0
- data/tasks/release.rake +15 -0
- data/tasks/rspec.rake +11 -0
- data/tasks/rvm.rake +43 -0
- data/test/ragel_profiler.rb +73 -0
- data/test/validate_fixtures.rb +74 -0
- 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')
|
data/lib/redcloth.rb
ADDED
@@ -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’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
|
+
"‘#{opts[:text]}’"
|
143
|
+
end
|
144
|
+
|
145
|
+
def quote2(opts)
|
146
|
+
"“#{opts[:text]}”"
|
147
|
+
end
|
148
|
+
|
149
|
+
def multi_paragraph_quote(opts)
|
150
|
+
"“#{opts[:text]}"
|
151
|
+
end
|
152
|
+
|
153
|
+
def ellipsis(opts)
|
154
|
+
"#{opts[:text]}…"
|
155
|
+
end
|
156
|
+
|
157
|
+
def emdash(opts)
|
158
|
+
"—"
|
159
|
+
end
|
160
|
+
|
161
|
+
def endash(opts)
|
162
|
+
" – "
|
163
|
+
end
|
164
|
+
|
165
|
+
def arrow(opts)
|
166
|
+
"→"
|
167
|
+
end
|
168
|
+
|
169
|
+
def dim(opts)
|
170
|
+
opts[:text].gsub!('x', '×')
|
171
|
+
opts[:text].gsub!("'", '′')
|
172
|
+
opts[:text].gsub!('"', '″')
|
173
|
+
opts[:text]
|
174
|
+
end
|
175
|
+
|
176
|
+
def trademark(opts)
|
177
|
+
"™"
|
178
|
+
end
|
179
|
+
|
180
|
+
def registered(opts)
|
181
|
+
"®"
|
182
|
+
end
|
183
|
+
|
184
|
+
def copyright(opts)
|
185
|
+
"©"
|
186
|
+
end
|
187
|
+
|
188
|
+
def entity(opts)
|
189
|
+
"&#{opts[:text]};"
|
190
|
+
end
|
191
|
+
|
192
|
+
def amp(opts)
|
193
|
+
"&"
|
194
|
+
end
|
195
|
+
|
196
|
+
def gt(opts)
|
197
|
+
">"
|
198
|
+
end
|
199
|
+
|
200
|
+
def lt(opts)
|
201
|
+
"<"
|
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
|
+
"""
|
214
|
+
end
|
215
|
+
|
216
|
+
def squot(opts)
|
217
|
+
"’"
|
218
|
+
end
|
219
|
+
|
220
|
+
def apos(opts)
|
221
|
+
"'"
|
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
|