breakout_parser 0.0.23 → 0.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/ChangeLog +19 -0
- data/README +23 -11
- data/ext/breakout_parser/extconf.rb +10 -5
- data/ext/breakout_parser/parser.h +27 -0
- data/ext/breakout_parser/parser.l +22 -6
- data/ext/breakout_parser/parser.y +294 -126
- data/ext/breakout_parser/ruby_ext.c +51 -22
- data/ext/breakout_parser/ruby_ext.h +2 -0
- data/spec/links_only_parser_spec.rb +916 -0
- data/spec/obj_proxy.rb +22 -0
- data/spec/parser_examples_spec.rb +115 -0
- data/spec/parser_spec.rb +146 -34
- data/spec/spec_helper.rb +1 -0
- metadata +89 -62
- data/ext/breakout_parser/lex.yy.c +0 -2996
- data/ext/breakout_parser/parser.tab.c +0 -2482
- data/ext/breakout_parser/parser.tab.h +0 -113
@@ -1,6 +1,13 @@
|
|
1
1
|
#ifdef RUBY_VERSION
|
2
2
|
|
3
3
|
#include "ruby.h"
|
4
|
+
#include "ruby_ext.h"
|
5
|
+
|
6
|
+
#ifdef RUBY_19
|
7
|
+
#include "ruby/st.h"
|
8
|
+
#else
|
9
|
+
#include "st.h"
|
10
|
+
#endif
|
4
11
|
|
5
12
|
void Init_breakout_parser();
|
6
13
|
VALUE method_parse(int, VALUE*, VALUE);
|
@@ -20,55 +27,78 @@ extern const char *space_name;
|
|
20
27
|
extern size_t in_buf_len, bufsize, space_name_len;
|
21
28
|
|
22
29
|
extern const char *site_url;
|
30
|
+
extern const char *large_files_url;
|
23
31
|
extern size_t site_url_len;
|
32
|
+
extern size_t large_files_url_len;
|
33
|
+
|
34
|
+
extern VALUE meta_attributes;
|
24
35
|
|
25
36
|
VALUE vcs_url;
|
26
37
|
|
38
|
+
char **temp_names, **temp_values;
|
39
|
+
|
27
40
|
extern int parse_links_only, absolute_urls;
|
28
41
|
|
29
42
|
VALUE do_parse(int argc, VALUE *argv, VALUE self) {
|
30
43
|
VALUE s, text, r_space_name;
|
31
44
|
char *p;
|
32
45
|
|
33
|
-
if(
|
34
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..
|
35
|
-
return rb_str_new("",0); // unreachable code, but for double safety
|
46
|
+
if (argc < 2 || argc > 7) {
|
47
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..7)", argc);
|
36
48
|
}
|
37
49
|
|
38
50
|
text = argv[0];
|
39
51
|
r_space_name = argv[1];
|
40
52
|
|
41
53
|
site_url = NULL; site_url_len = 0;
|
42
|
-
if(
|
54
|
+
if (argc > 2 && RTEST(argv[2])){
|
43
55
|
site_url = StringValueCStr(argv[2]);
|
44
56
|
site_url_len = site_url ? strlen(site_url) : 0;
|
45
57
|
while( site_url && site_url_len > 0 && site_url[site_url_len-1] == '/' ) {
|
46
58
|
// skip trailing slashes
|
47
|
-
site_url_len--;
|
59
|
+
site_url_len--;
|
48
60
|
}
|
49
61
|
}
|
50
62
|
|
51
|
-
vcs_url =
|
52
|
-
if(
|
63
|
+
vcs_url = rb_str_new2("");
|
64
|
+
if (argc > 3 && RTEST(argv[3])) {
|
53
65
|
vcs_url = argv[3];
|
54
66
|
}
|
55
67
|
|
56
68
|
absolute_urls = 0;
|
57
|
-
if(
|
69
|
+
if (argc > 4 && RTEST(argv[4])) {
|
58
70
|
absolute_urls = 1;
|
59
71
|
}
|
60
72
|
|
61
|
-
|
62
|
-
|
63
|
-
|
73
|
+
large_files_url = NULL; large_files_url_len = 0;
|
74
|
+
if (argc > 5 && RTEST(argv[5])) {
|
75
|
+
large_files_url = StringValueCStr(argv[5]);
|
76
|
+
large_files_url_len = large_files_url ? strlen(large_files_url) : 0;
|
64
77
|
}
|
65
78
|
|
66
|
-
|
67
|
-
|
79
|
+
meta_attributes = rb_hash_new();
|
80
|
+
if (argc > 6 && RTEST(argv[6])) {
|
81
|
+
// More information about object types may be found in ruby.h
|
82
|
+
if (TYPE(argv[6]) != T_HASH) {
|
83
|
+
rb_raise(rb_eTypeError, "wrong type of meta attributes values; expected Hash, given %d", TYPE(argv[6]));
|
84
|
+
}
|
85
|
+
|
86
|
+
if (RHASH_SIZE(argv[6]) > 0) {
|
87
|
+
meta_attributes = argv[6];
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
if(!text || !(RTEST(text))){
|
92
|
+
// empty input string
|
93
|
+
return rb_str_new2("");
|
94
|
+
}
|
95
|
+
|
96
|
+
p = RSTRING_PTR(text);
|
97
|
+
in_buf_len = RSTRING_LEN(text);
|
68
98
|
|
69
99
|
if(!p || in_buf_len <= 0){
|
70
100
|
// empty input string
|
71
|
-
return
|
101
|
+
return rb_str_new2("");
|
72
102
|
}
|
73
103
|
|
74
104
|
while( in_buf_len > 0 && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')){
|
@@ -84,25 +114,24 @@ VALUE do_parse(int argc, VALUE *argv, VALUE self) {
|
|
84
114
|
bufsize = 1 + in_buf_len + in_buf_len/3; // reserve 30% of in_buf size
|
85
115
|
if(bufsize<0x100) bufsize = 0x100;
|
86
116
|
|
87
|
-
buf = ALLOC_N(char, bufsize);
|
117
|
+
buf = ALLOC_N(char, bufsize);
|
88
118
|
bufptr = buf;
|
89
119
|
|
90
|
-
// protect buf from GC (theoretically)
|
91
|
-
rb_iv_set(self,"@obj",Data_Wrap_Struct(rb_cData,NULL,NULL,buf));
|
92
|
-
|
93
120
|
yyparse();
|
94
121
|
yylex_destroy();
|
95
|
-
//
|
122
|
+
// printf("[.] yyparse() ended\n");
|
96
123
|
|
97
124
|
// make ruby string from our char[] data
|
98
|
-
s = rb_str_new(buf,bufptr-buf);
|
125
|
+
s = rb_str_new(buf, bufptr-buf);
|
99
126
|
|
100
|
-
// cleanup
|
101
|
-
rb_iv_set(self,"@obj",Qnil);
|
102
127
|
xfree(buf);
|
103
128
|
buf = bufptr = NULL;
|
104
129
|
bufsize = 0;
|
105
130
|
|
131
|
+
#ifdef RUBY_19
|
132
|
+
s = rb_funcall(s, rb_intern("force_encoding"), 1, rb_str_new2("UTF-8")); // encode string to UTF-8
|
133
|
+
#endif
|
134
|
+
|
106
135
|
return s;
|
107
136
|
}
|
108
137
|
|
@@ -0,0 +1,916 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require File.dirname(__FILE__) + '/obj_proxy'
|
4
|
+
|
5
|
+
describe 'BreakoutParser' do
|
6
|
+
def self.hex_string s
|
7
|
+
## sexier, but not runs on ruby 1.8.6 patchlevel 383 i386-mingw32:
|
8
|
+
#s.each_byte.to_a.map{ |c| "%02x" % c }.join
|
9
|
+
r = ''
|
10
|
+
s.each_byte{ |c| r << "%02x" % c }
|
11
|
+
r
|
12
|
+
end
|
13
|
+
def hex_string s; self.class.hex_string(s); end
|
14
|
+
|
15
|
+
it 'accepts from 2 to 7 arguments' do
|
16
|
+
expect { BreakoutParser.parse }.to raise_error(ArgumentError, "wrong number of arguments (0 for 2..7)")
|
17
|
+
expect { BreakoutParser.parse('a') }.to raise_error(ArgumentError, "wrong number of arguments (1 for 2..7)")
|
18
|
+
[9, 10].each do |argc|
|
19
|
+
expect { BreakoutParser.parse(*(['a'] * (argc - 1) + [{}])) }.to raise_error(ArgumentError, "wrong number of arguments (#{argc} for 2..7)")
|
20
|
+
end
|
21
|
+
(2..6).each do |argc|
|
22
|
+
expect { BreakoutParser.parse(*(['a'] * argc)) }.to_not raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
expect { BreakoutParser.parse(*(['a'] * 6 + [{}])) }.to_not raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'converts \n to <br />' do
|
28
|
+
parse("aaa\nbbb").should match(%r"aaa ?<br /> ?bbb")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "parses 1M file #1" do
|
32
|
+
s = 'a' * 1024 * 1024
|
33
|
+
parse(s).size.should == s.size
|
34
|
+
end
|
35
|
+
|
36
|
+
it "parses 1M file #2" do
|
37
|
+
s = 'a' + (' ' * 1024 * 1024) + 'b'
|
38
|
+
parse(s).should == 'a b'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "parses 1M file #3" do
|
42
|
+
s = 'a ' * 1024 * 512
|
43
|
+
parse(s).size.should == s.strip.size
|
44
|
+
end
|
45
|
+
|
46
|
+
it "handles nil & false text well" do
|
47
|
+
parse(false).should == ""
|
48
|
+
parse(false, :space_name => false).should == ""
|
49
|
+
parse("", :space_name => false).should == ""
|
50
|
+
parse(nil).should == ""
|
51
|
+
parse(nil, :space_name => nil).should == ""
|
52
|
+
parse("", :space_name => nil).should == ""
|
53
|
+
end
|
54
|
+
|
55
|
+
it "handles nil space_name well" do
|
56
|
+
lambda{
|
57
|
+
parse("#123", :space_name => nil)
|
58
|
+
}.should raise_error(TypeError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "handles false space_name well" do
|
62
|
+
lambda{
|
63
|
+
parse("#123", :space_name => false)
|
64
|
+
}.should raise_error(TypeError)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "strips tailing spaces and newlines" do
|
68
|
+
parse("aaa ").should == "aaa"
|
69
|
+
parse("aaa\t\t\t\t\t\t").should == "aaa"
|
70
|
+
parse("aaa\r\r\r\r\r").should == "aaa"
|
71
|
+
parse("aaa\n\n\n\n\n").should == "aaa"
|
72
|
+
parse("aaa\r\n\r\n\r\n\r\n").should == "aaa"
|
73
|
+
parse("aaa\r\n\t \t \n \r \n \t \t\n\r ").should == "aaa"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "strips leading spaces and newlines" do
|
77
|
+
parse(" aaa").should == "aaa"
|
78
|
+
parse("\t\t\t\t\t\taaa").should == "aaa"
|
79
|
+
parse("\r\r\r\r\raaa").should == "aaa"
|
80
|
+
parse("\n\n\n\n\naaa").should == "aaa"
|
81
|
+
parse("\r\n\r\n\r\n\r\naaa").should == "aaa"
|
82
|
+
parse("\r\n\t \t \n \r \n \t \t\n\r aaa").should == "aaa"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "converts each newline to <br />" do
|
86
|
+
parse("aaa\n\nbbb").should == "aaa<br /><br />bbb"
|
87
|
+
parse("aaa\n \nbbb").should == "aaa<br /><br />bbb"
|
88
|
+
parse("aaa\n\n\nbbb").should == "aaa<br /><br /><br />bbb"
|
89
|
+
parse("aaa\n \n \nbbb").should == "aaa<br /><br /><br />bbb"
|
90
|
+
parse("aaa\r\n \r\n \r\nbbb").should == "aaa<br /><br /><br />bbb"
|
91
|
+
parse("aaa\n \n\n \nbbb").should == "aaa<br /><br /><br /><br />bbb"
|
92
|
+
parse("aaa\n \n\n\n \nbbb").should == "aaa" + "<br />"*5 + "bbb"
|
93
|
+
parse("aaa\n\n\n\n\n\n\nbbb").should == "aaa" + "<br />"*7 + "bbb"
|
94
|
+
parse("aaa\r\n\r\n\r\nbbb").should == "aaa" + "<br />"*3 + "bbb"
|
95
|
+
end
|
96
|
+
|
97
|
+
###############################################################################
|
98
|
+
|
99
|
+
describe "@code@" do
|
100
|
+
it "only" do
|
101
|
+
parse("@smth@").should == '@smth@'
|
102
|
+
end
|
103
|
+
it "at beginning" do
|
104
|
+
parse("@smth@\nxxx").should == '@smth@<br />xxx'
|
105
|
+
end
|
106
|
+
it "in the middle of text" do
|
107
|
+
parse("xxx @smth@ yyy").should == 'xxx @smth@ yyy'
|
108
|
+
end
|
109
|
+
it "parses @multiline\\nsmth@" do
|
110
|
+
parse("@multiline\nsmth@").should == "@multiline<br />smth@"
|
111
|
+
end
|
112
|
+
it "not confuses" do
|
113
|
+
parse("look at @this code@ and mail me at xxx@yyy.com").should ==
|
114
|
+
'look at @this code@ and mail me at <a href="mailto:xxx@yyy.com">xxx@yyy.com</a>'
|
115
|
+
end
|
116
|
+
it "w/o closing tag" do
|
117
|
+
parse("@smth").should == '@smth'
|
118
|
+
end
|
119
|
+
it "nesting1 w/o closing tags" do
|
120
|
+
parse("@smth1 @smth2").should == '@smth1 @smth2'
|
121
|
+
end
|
122
|
+
it "nesting2 w/o closing tags" do
|
123
|
+
parse("@smth1 @smth2").should == '@smth1 @smth2'
|
124
|
+
end
|
125
|
+
it "two times" do
|
126
|
+
parse("@code1@ @code2@").should == "@code1@ @code2@"
|
127
|
+
parse("@code1@ @code2@").should == "@code1@ @code2@"
|
128
|
+
parse(" @code1@ @code2@ ").should == "@code1@ @code2@"
|
129
|
+
parse(" @code1@ xxx @code2@ ").should == "@code1@ xxx @code2@"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
###############################################################################
|
134
|
+
|
135
|
+
describe "*bold*" do
|
136
|
+
it "only" do
|
137
|
+
parse("*bold*").should == '*bold*'
|
138
|
+
end
|
139
|
+
it "at beginning" do
|
140
|
+
parse("*bold*\nxxx").should == "*bold*<br />xxx"
|
141
|
+
end
|
142
|
+
it "in the middle of text" do
|
143
|
+
parse("xxx *bold* yyy").should == "xxx *bold* yyy"
|
144
|
+
end
|
145
|
+
it "parses *multiline\\nbold*" do
|
146
|
+
parse("*multiline\nbold*").should == "*multiline<br />bold*"
|
147
|
+
end
|
148
|
+
it "skips lone star inside bold block" do
|
149
|
+
parse("*aaa * bbb*").should == "*aaa * bbb*"
|
150
|
+
end
|
151
|
+
it "skips lone star" do
|
152
|
+
parse("aaa * bbb").should == 'aaa * bbb'
|
153
|
+
end
|
154
|
+
it "w/o closing tag" do
|
155
|
+
parse("*bold").should == '*bold'
|
156
|
+
end
|
157
|
+
it "nesting1 w/o closing tags" do
|
158
|
+
parse("*bold1 *bold2").should == "*bold1 *bold2"
|
159
|
+
end
|
160
|
+
it "nesting2 w/o closing tags" do
|
161
|
+
parse("*bold1 *bold2").should == "*bold1 *bold2"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "not parses '*.*'" do
|
165
|
+
parse("*.*").should == "*.*"
|
166
|
+
parse(" *.* ").should == "*.*"
|
167
|
+
parse("aaa *.* bbb").should == "aaa *.* bbb"
|
168
|
+
end
|
169
|
+
|
170
|
+
it "not parses '*.something'" do
|
171
|
+
parse("*.exe").should == "*.exe"
|
172
|
+
parse(" *.exe ").should == "*.exe"
|
173
|
+
parse("aaa *.exe bbb").should == "aaa *.exe bbb"
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
###############################################################################
|
179
|
+
|
180
|
+
describe "_italic_" do
|
181
|
+
it "only" do
|
182
|
+
s = "_italic_"
|
183
|
+
parse(s).should == s
|
184
|
+
end
|
185
|
+
it "at beginning" do
|
186
|
+
s = "_italic_\nxxx"
|
187
|
+
parse(s).should == s.gsub("\n","<br />")
|
188
|
+
end
|
189
|
+
it "in the middle of text" do
|
190
|
+
s = "xxx _italic_ yyy"
|
191
|
+
parse(s).should == s
|
192
|
+
end
|
193
|
+
it "parses _multiline\\nitalic_" do
|
194
|
+
s = "_multiline\nitalic_"
|
195
|
+
parse(s).should == s.gsub("\n","<br />")
|
196
|
+
end
|
197
|
+
it "skips lone underscore inside italic block" do
|
198
|
+
s = "_aaa _ bbb_"
|
199
|
+
parse(s).should == s
|
200
|
+
end
|
201
|
+
it "skips lone underscore" do
|
202
|
+
s = "aaa _ bbb"
|
203
|
+
parse(s).should == s
|
204
|
+
end
|
205
|
+
it "w/o closing tag" do
|
206
|
+
s = "_italic"
|
207
|
+
parse(s).should == s
|
208
|
+
end
|
209
|
+
it "nesting1 w/o closing tags" do
|
210
|
+
s = "_italic1 _italic2"
|
211
|
+
parse(s).should == s
|
212
|
+
end
|
213
|
+
it "nesting2 w/o closing tags" do
|
214
|
+
s = "_italic1 _italic2"
|
215
|
+
parse(s).should == s.gsub(/ +/,' ')
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
###############################################################################
|
220
|
+
|
221
|
+
describe "combinations" do
|
222
|
+
it "bold in italic" do
|
223
|
+
s = "_aaa *bbb* ccc_"
|
224
|
+
parse(s).should == s
|
225
|
+
end
|
226
|
+
it "bold in italic - no closing1" do
|
227
|
+
s = "_aaa *bbb* ccc"
|
228
|
+
parse(s).should == s
|
229
|
+
end
|
230
|
+
it "bold in italic - no closing2" do
|
231
|
+
s = "_aaa *bbb ccc"
|
232
|
+
parse(s).should == s
|
233
|
+
end
|
234
|
+
|
235
|
+
it "italic in bold" do
|
236
|
+
s = "*aaa _bbb_ ccc*"
|
237
|
+
parse(s).should == s
|
238
|
+
end
|
239
|
+
it "italic in bold - no closing1" do
|
240
|
+
s = "*aaa _bbb_ ccc"
|
241
|
+
parse(s).should == s
|
242
|
+
end
|
243
|
+
it "italic in bold - no closing2" do
|
244
|
+
s = "*aaa _bbb ccc"
|
245
|
+
parse(s).should == s
|
246
|
+
end
|
247
|
+
|
248
|
+
{'ul' => '*', 'ol' => '#'}.each do |l,c|
|
249
|
+
it "raw text link inside #{l.upcase}> #1" do
|
250
|
+
s = "#{c} aaa http://www.ru"
|
251
|
+
parse(s).should == "#{c} aaa <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a>"
|
252
|
+
end
|
253
|
+
it "raw text link inside #{l.upcase}> #2" do
|
254
|
+
s = "#{c} aaa http://www.ru\n#{c} bbb"
|
255
|
+
parse(s).should == "#{c} aaa <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a><br />#{c} bbb"
|
256
|
+
end
|
257
|
+
it "raw text link inside #{l.upcase}> #3" do
|
258
|
+
s = "#{c} http://www.ru"
|
259
|
+
parse(s).should == "#{c} <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a>"
|
260
|
+
end
|
261
|
+
it "raw text link inside #{l.upcase}> #4" do
|
262
|
+
s = "#{c} aaa http://www.ru bbb"
|
263
|
+
parse(s).should == "#{c} aaa <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a> bbb"
|
264
|
+
end
|
265
|
+
it "two links inside #{l.upcase}>" do
|
266
|
+
s = "#{c} aaa http://www.ru http://ya.ru bbb"
|
267
|
+
parse(s).should == "#{c} aaa <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a> <a rel=\"nofollow\" href=\"http://ya.ru\">http://ya.ru</a> bbb"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
###############################################################################
|
273
|
+
|
274
|
+
describe "IGNORES unnumbered lists" do
|
275
|
+
it "should ignore" do
|
276
|
+
parse("* a\n* b\n* c").should == "* a<br />* b<br />* c"
|
277
|
+
end
|
278
|
+
it "two lists" do
|
279
|
+
s = "* a\n* b\n* c"
|
280
|
+
s = s + "\nxxx\n" + s
|
281
|
+
parse(s).should == "* a<br />* b<br />* c<br />xxx<br />* a<br />* b<br />* c"
|
282
|
+
end
|
283
|
+
it "in middle of text when begins with space" do
|
284
|
+
parse("hello\n * a\n * b\n * c\nworld").should ==
|
285
|
+
"hello<br />* a<br />* b<br />* c<br />world"
|
286
|
+
end
|
287
|
+
it "in middle of text" do
|
288
|
+
parse("hello\n* a\n* b\n* c\nworld").should ==
|
289
|
+
"hello<br />* a<br />* b<br />* c<br />world"
|
290
|
+
end
|
291
|
+
it "after blank line" do
|
292
|
+
parse("hello\n\n * a\n * b\n * c\nworld").should ==
|
293
|
+
"hello<br /><br />* a<br />* b<br />* c<br />world"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
###############################################################################
|
298
|
+
|
299
|
+
describe "IGNORES numbered lists" do
|
300
|
+
it "should ignore" do
|
301
|
+
parse("# a\n# b\n# c").should == "# a<br /># b<br /># c"
|
302
|
+
end
|
303
|
+
it "two lists" do
|
304
|
+
s = "# a\n# b\n# c"
|
305
|
+
s = s + "\nxxx\n" + s
|
306
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
307
|
+
end
|
308
|
+
it "in middle of text when begins with space" do
|
309
|
+
parse("hello\n # a\n # b\n # c\nworld").should ==
|
310
|
+
"hello<br /># a<br /># b<br /># c<br />world"
|
311
|
+
end
|
312
|
+
it "in middle of text" do
|
313
|
+
parse("hello\n# a\n# b\n# c\nworld").should ==
|
314
|
+
"hello<br /># a<br /># b<br /># c<br />world"
|
315
|
+
end
|
316
|
+
it "after blank line" do
|
317
|
+
parse("hello\n\n # a\n # b\n # c\nworld").should ==
|
318
|
+
"hello<br /><br /># a<br /># b<br /># c<br />world"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
###############################################################################
|
323
|
+
|
324
|
+
1.upto(5) do |lvl|
|
325
|
+
describe "H#{lvl}" do
|
326
|
+
it "at the beginning" do
|
327
|
+
s = "h#{lvl}. xxx"
|
328
|
+
parse(s).should == h(s)
|
329
|
+
end
|
330
|
+
it "after 1 line of text" do
|
331
|
+
s = "abcd\nh#{lvl}. xxx"
|
332
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
333
|
+
end
|
334
|
+
it "after 2 lines of text" do
|
335
|
+
s = "abcd\ndefgh\nh#{lvl}. xxx"
|
336
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
337
|
+
end
|
338
|
+
it "in middle of other words" do
|
339
|
+
s = "abcd defgh h#{lvl}. xxx yyy"
|
340
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
341
|
+
end
|
342
|
+
it "in middle of other lines" do
|
343
|
+
s = "abcd defgh\nh#{lvl}. xxx\nyyy"
|
344
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
345
|
+
end
|
346
|
+
|
347
|
+
it "does nothing special on spaces in id" do
|
348
|
+
s = "h#{lvl}. xxx yyy z"
|
349
|
+
parse(s).should == h(s).gsub(/ +/,' ')
|
350
|
+
end
|
351
|
+
it "does nothing special on underscores in id" do
|
352
|
+
s = "h#{lvl}. xxx___yyy_z"
|
353
|
+
parse(s).should == h(s)
|
354
|
+
end
|
355
|
+
it "does nothing special on dashes in id" do
|
356
|
+
s = "h#{lvl}. xxx---yyy-z"
|
357
|
+
parse(s).should == h(s)
|
358
|
+
end
|
359
|
+
it "does nothing special on dots in id" do
|
360
|
+
s = "h#{lvl}. xxx...yyy.z"
|
361
|
+
parse(s).should == h(s)
|
362
|
+
end
|
363
|
+
|
364
|
+
%w'Ъ ъ : ; , привет" \' ! < >'.each do |c|
|
365
|
+
it "does nothing special on \"#{c}\"" do
|
366
|
+
s = "h#{lvl}. xxx#{c}yyy"
|
367
|
+
parse(s).should == h(s)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
###############################################################################
|
374
|
+
|
375
|
+
describe "raw text links" do
|
376
|
+
describe "starting with 'http://'" do
|
377
|
+
it "at the beginning" do
|
378
|
+
parse("http://asd.ru").should == "<a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a>"
|
379
|
+
end
|
380
|
+
it "in middle of other words" do
|
381
|
+
parse("aaa bbb ccc http://asd.ru ddd eee fff").should ==
|
382
|
+
"aaa bbb ccc <a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a> ddd eee fff"
|
383
|
+
end
|
384
|
+
it "in new line" do
|
385
|
+
parse("aaa bbb ccc\nhttp://asd.ru\nddd eee fff").should match(
|
386
|
+
%r"aaa bbb ccc ?<br /> ?<a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a> ?<br /> ?ddd eee fff"
|
387
|
+
)
|
388
|
+
end
|
389
|
+
it "escapes '&' in link _text_" do
|
390
|
+
parse("http://asd.ru/?a=1&b=2").should == "<a rel=\"nofollow\" href=\"http://asd.ru/?a=1&b=2\">http://asd.ru/?a=1&b=2</a>"
|
391
|
+
end
|
392
|
+
|
393
|
+
it "parses https://" do
|
394
|
+
parse("https://asd.ru").should == "<a rel=\"nofollow\" href=\"https://asd.ru\">https://asd.ru</a>"
|
395
|
+
end
|
396
|
+
|
397
|
+
%w', .'.each do |c|
|
398
|
+
it "stops parsing on \"#{c} \"" do
|
399
|
+
parse("http://asd.ru#{c}").should == "<a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a>#{c}"
|
400
|
+
parse(" http://asd.ru#{c} ").should == "<a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a>#{c}"
|
401
|
+
parse(" http://asd.ru#{c} hello!").should == "<a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a>#{c} hello!"
|
402
|
+
parse("xxx http://asd.ru#{c} hello!").should == "xxx <a rel=\"nofollow\" href=\"http://asd.ru\">http://asd.ru</a>#{c} hello!"
|
403
|
+
parse(" http://asd.ru/#{c} hello!").should == "<a rel=\"nofollow\" href=\"http://asd.ru/\">http://asd.ru/</a>#{c} hello!"
|
404
|
+
parse(" http://aaa.com#{c} http://bbb.com").should ==
|
405
|
+
"<a rel=\"nofollow\" href=\"http://aaa.com\">http://aaa.com</a>#{c} <a rel=\"nofollow\" href=\"http://bbb.com\">http://bbb.com</a>"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe "starting with 'www.'" do
|
411
|
+
it "at the beginning" do
|
412
|
+
parse("www.ru").should == "<a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a>"
|
413
|
+
end
|
414
|
+
it "in middle of other words" do
|
415
|
+
parse("aaa bbb ccc www.ru ddd eee fff").should ==
|
416
|
+
"aaa bbb ccc <a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a> ddd eee fff"
|
417
|
+
end
|
418
|
+
it "in new line" do
|
419
|
+
parse("aaa bbb ccc\nwww.ru\nddd eee fff").should match(
|
420
|
+
%r"aaa bbb ccc ?<br /> ?<a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a> ?<br /> ?ddd eee fff"
|
421
|
+
)
|
422
|
+
end
|
423
|
+
it "escapes '&' in link _text_" do
|
424
|
+
parse("www.ru/?a=1&b=2").should == "<a rel=\"nofollow\" href=\"http://www.ru/?a=1&b=2\">www.ru/?a=1&b=2</a>"
|
425
|
+
end
|
426
|
+
|
427
|
+
%w', .'.each do |c|
|
428
|
+
it "stops parsing on \"#{c} \"" do
|
429
|
+
parse("www.ru#{c}").should == "<a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a>#{c}"
|
430
|
+
parse(" www.ru#{c} ").should == "<a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a>#{c}"
|
431
|
+
parse(" www.ru#{c} hello!").should == "<a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a>#{c} hello!"
|
432
|
+
parse("xxx www.ru#{c} hello!").should == "xxx <a rel=\"nofollow\" href=\"http://www.ru\">www.ru</a>#{c} hello!"
|
433
|
+
parse(" www.ru/#{c} hello!").should == "<a rel=\"nofollow\" href=\"http://www.ru/\">www.ru/</a>#{c} hello!"
|
434
|
+
parse(" www.aaa.com#{c} www.bbb.com").should ==
|
435
|
+
"<a rel=\"nofollow\" href=\"http://www.aaa.com\">www.aaa.com</a>#{c} <a rel=\"nofollow\" href=\"http://www.bbb.com\">www.bbb.com</a>"
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
describe 'e-mails' do
|
441
|
+
it "at the beginning" do
|
442
|
+
parse("aaa@bbb.com").should == "<a href=\"mailto:aaa@bbb.com\">aaa@bbb.com</a>"
|
443
|
+
end
|
444
|
+
it "in middle of other words" do
|
445
|
+
parse("aaa bbb ccc xx@yy.cn ddd eee fff").should ==
|
446
|
+
"aaa bbb ccc <a href=\"mailto:xx@yy.cn\">xx@yy.cn</a> ddd eee fff"
|
447
|
+
end
|
448
|
+
it "in new line" do
|
449
|
+
parse("aaa bbb ccc\naa.bb@cc.dd.ee\nddd eee fff").should match(
|
450
|
+
%r"aaa bbb ccc ?<br /> ?<a href=\"mailto:aa.bb@cc.dd.ee\">aa.bb@cc.dd.ee</a> ?<br /> ?ddd eee fff"
|
451
|
+
)
|
452
|
+
end
|
453
|
+
|
454
|
+
%w', .'.each do |c|
|
455
|
+
it "stops parsing on \"#{c} \"" do
|
456
|
+
parse("a-b@c-d.efghjikl#{c}").should == "<a href=\"mailto:a-b@c-d.efghjikl\">a-b@c-d.efghjikl</a>#{c}"
|
457
|
+
parse(" a-b@c-d.efghjikl#{c} ").should == "<a href=\"mailto:a-b@c-d.efghjikl\">a-b@c-d.efghjikl</a>#{c}"
|
458
|
+
parse(" a-b@c-d.efghjikl#{c} hello!").should == "<a href=\"mailto:a-b@c-d.efghjikl\">a-b@c-d.efghjikl</a>#{c} hello!"
|
459
|
+
parse("xxx a-b@c-d.efghjikl#{c} hello!").should == "xxx <a href=\"mailto:a-b@c-d.efghjikl\">a-b@c-d.efghjikl</a>#{c} hello!"
|
460
|
+
parse(" a-b@c-d.efghjikl#{c} hello!").should == "<a href=\"mailto:a-b@c-d.efghjikl\">a-b@c-d.efghjikl</a>#{c} hello!"
|
461
|
+
parse(" www@aaa.com#{c} www@bbb.com").should ==
|
462
|
+
"<a href=\"mailto:www@aaa.com\">www@aaa.com</a>#{c} <a href=\"mailto:www@bbb.com\">www@bbb.com</a>"
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
it "not parses bad emails" do
|
467
|
+
s="a@b.c a@b a.b@c a.b@@c a@b@c.d a#b@c.d"
|
468
|
+
parse(s).should == s
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
###############################################################################
|
474
|
+
|
475
|
+
describe "#ticketNum ticket links" do
|
476
|
+
it "at the beginning" do
|
477
|
+
parse("#1234").should == '<a href="/spaces/test_space/tickets/1234">#1234</a>'
|
478
|
+
end
|
479
|
+
it "in middle of other words" do
|
480
|
+
parse("aaa bbb ccc #3476 ddd eee fff").should ==
|
481
|
+
'aaa bbb ccc <a href="/spaces/test_space/tickets/3476">#3476</a> ddd eee fff'
|
482
|
+
end
|
483
|
+
it "in new line" do
|
484
|
+
parse("aaa bbb ccc\n#1234\nddd eee fff").should match(
|
485
|
+
%r|aaa bbb ccc ?<br /> ?<a href="/spaces/test_space/tickets/1234">#1234</a> ?<br /> ?ddd eee fff|
|
486
|
+
)
|
487
|
+
end
|
488
|
+
it "ignores non-digits" do
|
489
|
+
parse("#1234d").should == '#1234d'
|
490
|
+
parse("#xxx").should == '#xxx'
|
491
|
+
end
|
492
|
+
|
493
|
+
".,;!?:-".each_char do |c|
|
494
|
+
it "uses '#{c}' as separator" do
|
495
|
+
l = '<a href="/spaces/test_space/tickets/1234">#1234</a>'
|
496
|
+
t = "L#{c}L"
|
497
|
+
parse(t.gsub('L','#1234')).should == t.gsub('L',l)
|
498
|
+
t = "L#{c}#{c}L"
|
499
|
+
parse(t.gsub('L','#1234')).should == t.gsub('L',l)
|
500
|
+
t = "#{c}L#{c}L#{c}"
|
501
|
+
parse(t.gsub('L','#1234')).should == t.gsub('L',l)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
###############################################################################
|
507
|
+
|
508
|
+
[
|
509
|
+
%w'<pre><code> </code></pre>',
|
510
|
+
%w'<pre> </pre>',
|
511
|
+
%w'<notextile> </notextile>'
|
512
|
+
].each do |ot,ct|
|
513
|
+
# ot - opening tag
|
514
|
+
# ct - closing tag
|
515
|
+
|
516
|
+
# if ot == '<notextile>'
|
517
|
+
# ote,cte = '',''
|
518
|
+
# else
|
519
|
+
ote,cte = ot,ct
|
520
|
+
# end
|
521
|
+
|
522
|
+
describe "#{ot}..#{ct}" do
|
523
|
+
it "works" do
|
524
|
+
s = <<-EOF
|
525
|
+
for ( n = 0; n < max_size && \
|
526
|
+
(c = getc( yyin )) != EOF && c != '\\n'; ++n ) \
|
527
|
+
buf[n] = (char) c; \
|
528
|
+
|
529
|
+
EOF
|
530
|
+
|
531
|
+
parse("#{ot}#{s.strip}#{ct}").should ==
|
532
|
+
h("#{ot}#{s.strip}#{ct}").gsub(/[ \t]+/,' ').gsub("\n","<br />")
|
533
|
+
|
534
|
+
s = <<-EOF
|
535
|
+
while ( 1 < 2 ) do
|
536
|
+
puts "<b>12345\\t54321</b>"
|
537
|
+
// *bold* comment
|
538
|
+
// _italic_ comment
|
539
|
+
end
|
540
|
+
---
|
541
|
+
* aaa
|
542
|
+
* bbb
|
543
|
+
* ccc
|
544
|
+
|
545
|
+
EOF
|
546
|
+
parse("#{ot}#{s.strip}#{ct}").should ==
|
547
|
+
h("#{ote}#{s.strip}#{cte}").gsub(/[ \t]+/,' ').gsub(/\n */,"<br />")
|
548
|
+
end
|
549
|
+
it "not parses *bold*" do
|
550
|
+
s = "#{ot} *bold*#{ct}"
|
551
|
+
parse(s).should == h(s)
|
552
|
+
end
|
553
|
+
it "not parses _italic_" do
|
554
|
+
s = "#{ot} _italic_#{ct}"
|
555
|
+
parse(s).should == h(s)
|
556
|
+
end
|
557
|
+
it "not parses UL lists" do
|
558
|
+
s = "#{ot}\n * l1\n * l2\n * l3#{ct}"
|
559
|
+
parse(s).should == h(s).gsub("\n ","<br />")
|
560
|
+
end
|
561
|
+
it "not parses OL lists" do
|
562
|
+
s = "#{ot}\n # l1\n # l2\n # l3#{ct}"
|
563
|
+
parse(s).should == h(s).gsub("\n ","<br />")
|
564
|
+
end
|
565
|
+
it "not parses H1..H5" do
|
566
|
+
1.upto(5) do |i|
|
567
|
+
s = "#{ot}\nh#{i}. zzzzzzz\n#{ct}"
|
568
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
569
|
+
end
|
570
|
+
end
|
571
|
+
it "parses raw text links" do
|
572
|
+
s = "#{ot}xxx http://www.ru yyy#{ct}"
|
573
|
+
parse(s).should == "#{h(ote)}xxx <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a> yyy#{h(cte)}"
|
574
|
+
s = "#{ot}http://www.ru#{ct}"
|
575
|
+
parse(s).should == "#{h(ote)}<a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a>#{h(cte)}"
|
576
|
+
end
|
577
|
+
it "keeps newlines" do
|
578
|
+
s = "#{ot}aaa\nbbb#{ct}"
|
579
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
580
|
+
s = "#{ot}aaa\n\nbbb\nccc#{ct}"
|
581
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
582
|
+
end
|
583
|
+
|
584
|
+
it "w/o closing tags" do
|
585
|
+
s = "#{ot}aaa"
|
586
|
+
parse(s).should == h(s)
|
587
|
+
end
|
588
|
+
|
589
|
+
it "in middle of text" do
|
590
|
+
s = "xxx #{ot}yyyy#{ct} jjj"
|
591
|
+
parse(s).should == h(s)
|
592
|
+
end
|
593
|
+
|
594
|
+
it "with 2 instances" do
|
595
|
+
s = "xxx #{ot}yyyy#{ct} <jjj> #{ot}asdkjaslkd#{ct} END"
|
596
|
+
parse(s).should == h(s)
|
597
|
+
end
|
598
|
+
|
599
|
+
it "works with unicode" do
|
600
|
+
s = "привет #{ot} жжж #{ct} пока!"
|
601
|
+
parse(s).should == h(s)
|
602
|
+
|
603
|
+
s = "#{ot}абвгдеёжзийклмнопрстуфхцчшщьыъэюя#{ct}"
|
604
|
+
parse(s).should == h(s)
|
605
|
+
|
606
|
+
s = "#{ot}АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ#{ct}"
|
607
|
+
parse(s).should == h(s)
|
608
|
+
|
609
|
+
s = "#{ot}☸☹☺☻☼☽☾☿#{ct}"
|
610
|
+
parse(s).should == h(s)
|
611
|
+
end
|
612
|
+
|
613
|
+
it "should escape lone closing tags" do
|
614
|
+
s = "#{ct}"
|
615
|
+
parse(s).should == h(s)
|
616
|
+
end
|
617
|
+
|
618
|
+
it "should skip newlines and spaces at end" do
|
619
|
+
s = "#{ot} aaa bbb ccc \n\n\n \t\n\n\n\r\n\r\n \t #{ct}"
|
620
|
+
parse(s).should == "#{h(ote)} aaa bbb ccc<br /><br /><br /><br /><br /><br /><br /><br />#{h(cte)}"
|
621
|
+
end
|
622
|
+
|
623
|
+
it "escapes html chars" do
|
624
|
+
HTML_ESCAPE.each do |k,v|
|
625
|
+
parse("#{ot}#{k}#{ct}").should == h("#{ote}#{k}#{cte}")
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
describe "NOT PARSES <pre><code>..</code></pre>" do
|
632
|
+
it "with no spaces between <pre> and <code>" do
|
633
|
+
s = "<pre><code>aaa</code></pre>"
|
634
|
+
parse(s).should == h(s).gsub(/ +/,' ')
|
635
|
+
end
|
636
|
+
|
637
|
+
it "with spaces between <pre> and <code>" do
|
638
|
+
s = "<pre> <code>aaa</code> </pre>"
|
639
|
+
parse(s).should == h(s).gsub(/ +/,' ')
|
640
|
+
end
|
641
|
+
it "with spaces between <pre> and <code> and inside" do
|
642
|
+
s = "<pre> <code> aaa bbb </code> </pre>"
|
643
|
+
parse(s).should == h(s).gsub(/ +/,' ')
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
###############################################################################
|
648
|
+
|
649
|
+
describe "<code>..</code>" do
|
650
|
+
it "keeps <code> tags" do
|
651
|
+
s = "<code>aaa</code>"
|
652
|
+
parse(s).should == h(s)
|
653
|
+
end
|
654
|
+
it "strips heading & tailing whitespace" do
|
655
|
+
s = "<code> \r\n \t \t\r aaa \r\n\t \t\r </code>"
|
656
|
+
parse(s).should == "<code><br /><br /> aaa<br /><br /> </code>"
|
657
|
+
end
|
658
|
+
it "not parses *bold*" do
|
659
|
+
s = "<code>aaa *bbb* ccc</code>"
|
660
|
+
parse(s).should == h(s)
|
661
|
+
end
|
662
|
+
it "not parses _italic_" do
|
663
|
+
s = "<code>aaa _bbb_ ccc</code>"
|
664
|
+
parse(s).should == h(s)
|
665
|
+
end
|
666
|
+
it "not parses headers" do
|
667
|
+
s = "<code>aaa\nh1. bbb\nccc</code>"
|
668
|
+
parse(s).should == h(s).gsub("\n","<br />")
|
669
|
+
end
|
670
|
+
it "not parses <pre>" do
|
671
|
+
s = "<code>aaa <pre>bbb</pre> ccc</code>"
|
672
|
+
parse(s).should == h(s)
|
673
|
+
end
|
674
|
+
it "NOT closes unclosed <code>" do
|
675
|
+
s = "aaa <code>bbb"
|
676
|
+
parse(s).should == h(s)
|
677
|
+
end
|
678
|
+
it "NOT escapes '&'" do
|
679
|
+
s = "<code>aaa & bbb</code>"
|
680
|
+
parse(s).should == h(s)
|
681
|
+
end
|
682
|
+
it "parses links" do
|
683
|
+
s = "<code>aaa #1 #2 #3 http://www.ru [[wiki:jjj]] [[url:http://www.ru]] bbb</code>"
|
684
|
+
parse(s).should == "<code>aaa <a href=\"/spaces/test_space/tickets/1\">#1</a> <a href=\"/spaces/test_space/tickets/2\">#2</a> <a href=\"/spaces/test_space/tickets/3\">#3</a> <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a> <a class=\"wiki_link\" title=\"jjj\" href=\"/spaces/test_space/wiki/jjj\">jjj</a> <a rel=\"nofollow\" href=\"http://www.ru\">http://www.ru</a> bbb</code>"
|
685
|
+
end
|
686
|
+
it "NOT works" do
|
687
|
+
s = "<code>aaa & bbb</code> xxx <code>jjj&hhh</code>"
|
688
|
+
parse(s).should == h(s)
|
689
|
+
s = "<code> aaa </code>xxx<code> jjj </code>"
|
690
|
+
parse(s).should == h(s).gsub(/ +/,' ')
|
691
|
+
end
|
692
|
+
it "NOT keeps code bold" do
|
693
|
+
s = "*aaa <code>bbb</code> ccc*"
|
694
|
+
parse(s).should == h(s)
|
695
|
+
s = "*<code>aaa</code>*"
|
696
|
+
parse(s).should == h(s)
|
697
|
+
end
|
698
|
+
it "NOT keeps code italic" do
|
699
|
+
s = "_aaa <code>bbb</code> ccc_"
|
700
|
+
parse(s).should == h(s)
|
701
|
+
s = "_<code>aaa</code>_"
|
702
|
+
parse(s).should == h(s)
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
###############################################################################
|
707
|
+
|
708
|
+
describe "Assembla Links" do
|
709
|
+
a = {}
|
710
|
+
a["wiki:Name"] = '<a class="wiki_link" title="Name" href="/spaces/test_space/wiki/Name">Name</a>'
|
711
|
+
a["Name"] = '<a class="wiki_link" title="Name" href="/spaces/test_space/wiki/Name">Name</a>'
|
712
|
+
a["Name#Ref"] = '<a class="wiki_link" title="Name#Ref" href="/spaces/test_space/wiki/Name#h-Ref">Name#Ref</a>'
|
713
|
+
a["Name#h-Ref"] = '<a class="wiki_link" title="Name#h-Ref" href="/spaces/test_space/wiki/Name#h-h-Ref">Name#h-Ref</a>'
|
714
|
+
a["#Ref"] = '<a href="#h-Ref" title="#Ref" class="wiki_link">#Ref</a>'
|
715
|
+
a["#привет"] = %Q|<a href="#h-#{hex_string("привет")}" title="#привет" class="wiki_link">#привет</a>|
|
716
|
+
a["#with spc"] = %Q|<a href="#h-with__spc" title="#with spc" class="wiki_link">#with spc</a>|
|
717
|
+
a["#with__usc"] = %Q|<a href="#h-with__usc" title="#with__usc" class="wiki_link">#with__usc</a>|
|
718
|
+
a["#with--dsh"] = %Q|<a href="#h-with--dsh" title="#with--dsh" class="wiki_link">#with--dsh</a>|
|
719
|
+
a["#with!xclm"] = %Q|<a href="#h-#{hex_string("with!xclm")}" title="#with!xclm" class="wiki_link">#with!xclm</a>|
|
720
|
+
a["#with&"] = %Q|<a href="#h-#{hex_string("with&")}" title="#with&" class="wiki_link">#with&amp</a>|
|
721
|
+
|
722
|
+
a["ticket:234"] = '<a href="/spaces/test_space/tickets/234">#234</a>'
|
723
|
+
a["revision:1f4bdab77be696efd"] =
|
724
|
+
'<a href="/code/test_space/git/changesets/1f4bdab77be696efd">revision:1f4bdab77be696efd</a>'
|
725
|
+
a["revision:12345"] =
|
726
|
+
'<a href="/code/test_space/subversion/changesets/12345">revision:12345</a>'
|
727
|
+
a["revision:567:1f4bdab77be696efd"] =
|
728
|
+
'<a href="/code/test_space/git-567/changesets/1f4bdab77be696efd">revision:1f4bdab77be696efd</a>'
|
729
|
+
a["revision:3:12345"] =
|
730
|
+
'<a href="/code/test_space/subversion-3/changesets/12345">revision:12345</a>'
|
731
|
+
a["r:2345"] = '<a href="/code/test_space/subversion/changesets/2345">revision:2345</a>'
|
732
|
+
a["r:2345ef"] = '<a href="/code/test_space/git/changesets/2345ef">revision:2345ef</a>'
|
733
|
+
a["r:10:2345"] = '<a href="/code/test_space/subversion-10/changesets/2345">revision:2345</a>'
|
734
|
+
a["r:1:2345ef"] = '<a href="/code/test_space/git-1/changesets/2345ef">revision:2345ef</a>'
|
735
|
+
|
736
|
+
a["url:http://www.ru"] = '<a rel="nofollow" href="http://www.ru">http://www.ru</a>'
|
737
|
+
a["url:https://www.ru"] = '<a rel="nofollow" href="https://www.ru">https://www.ru</a>'
|
738
|
+
a["url:www.ru"] = '<a rel="nofollow" href="http://www.ru">http://www.ru</a>'
|
739
|
+
a["url:www.ru/?a=1&b=2"] = '<a rel="nofollow" href="http://www.ru/?a=1&b=2">http://www.ru/?a=1&b=2</a>'
|
740
|
+
a["url:ftp://www.ru"] = '<a rel="nofollow" href="ftp://www.ru">ftp://www.ru</a>'
|
741
|
+
a["url:/spaces/x2"] = '<a rel="nofollow" href="/spaces/x2">/spaces/x2</a>'
|
742
|
+
|
743
|
+
a["file:ExistingFile.txt"] =
|
744
|
+
'<a href="/spaces/test_space/documents/download/ExistingFile.txt">file:ExistingFile.txt</a>'
|
745
|
+
a["file:cVJUz6ejWr35pEab_qKWB8"] =
|
746
|
+
'<a href="/spaces/test_space/documents/download/cVJUz6ejWr35pEab_qKWB8">file:cVJUz6ejWr35pEab_qKWB8</a>'
|
747
|
+
|
748
|
+
a.each do |k,v|
|
749
|
+
it "parses [[#{k}]]" do
|
750
|
+
parse("[[#{k}]]").should == v
|
751
|
+
end
|
752
|
+
it "parses [[#{k}|привет тест]]" do
|
753
|
+
parse("[[#{k}|привет тест]]").should == v.sub(/>.*</,">привет тест<")
|
754
|
+
end
|
755
|
+
it "parses [[#{k}|test & here]]" do
|
756
|
+
parse("[[#{k}|test & here]]").should == v.sub(/>.*</,">test & here<")
|
757
|
+
end
|
758
|
+
if v['href="/'] && !k['url:']
|
759
|
+
it "parses [[#{k}]] with a site url" do
|
760
|
+
site_url = "http://www.ru"
|
761
|
+
parse("[[#{k}]]", :site_url => site_url).should ==
|
762
|
+
v.gsub('href="/',"href=\"#{site_url}/")
|
763
|
+
|
764
|
+
# with extraordinary slash
|
765
|
+
parse("[[#{k}]]", :site_url => "#{site_url}/").should ==
|
766
|
+
v.gsub('href="/',"href=\"#{site_url}/")
|
767
|
+
|
768
|
+
site_url = "http://127.0.0.1:3000"
|
769
|
+
parse("[[#{k}]]", :site_url => site_url).should ==
|
770
|
+
v.gsub('href="/',"href=\"#{site_url}/")
|
771
|
+
|
772
|
+
# with extraordinary slash
|
773
|
+
parse("[[#{k}]]", :site_url => "#{site_url}/").should ==
|
774
|
+
v.gsub('href="/',"href=\"#{site_url}/")
|
775
|
+
end
|
776
|
+
end
|
777
|
+
if v['/git/']
|
778
|
+
it "parses [[#{k}]] with custom git_url (String)" do
|
779
|
+
git_url = "http://www.ru/"
|
780
|
+
rev = k.split(':').last.tr(']','')
|
781
|
+
parse("[[#{k}]]", :git_url => git_url).should ==
|
782
|
+
v.sub('/code/test_space/git/changesets/',git_url)
|
783
|
+
end
|
784
|
+
|
785
|
+
it "parses [[#{k}]] with custom git_url (ObjProxy)" do
|
786
|
+
rev = k.split(':').last.tr(']','')
|
787
|
+
@asdfg = 'http://mmm.us'
|
788
|
+
git_url = Breakout::ObjProxy.new do
|
789
|
+
@asdfg + '/'
|
790
|
+
end
|
791
|
+
parse("[[#{k}]]", :git_url => git_url).should ==
|
792
|
+
v.sub('/code/test_space/git/changesets/',git_url)
|
793
|
+
end
|
794
|
+
|
795
|
+
it "parses [[#{k}]] with NULL git_url (ObjProxy)" do
|
796
|
+
rev = k.split(':').last.tr(']','')
|
797
|
+
git_url = Breakout::ObjProxy.new do
|
798
|
+
nil
|
799
|
+
end
|
800
|
+
parse("[[#{k}]]", :git_url => git_url).should == v
|
801
|
+
end
|
802
|
+
|
803
|
+
it "parses [[#{k}]] with FALSE git_url (ObjProxy)" do
|
804
|
+
rev = k.split(':').last.tr(']','')
|
805
|
+
git_url = Breakout::ObjProxy.new do
|
806
|
+
false
|
807
|
+
end
|
808
|
+
parse("[[#{k}]]", :git_url => git_url).should == v
|
809
|
+
end
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
it "should not instantiate ObjProxy's internal object if there are no git or svn links in text" do
|
814
|
+
git_url = Breakout::ObjProxy.new do
|
815
|
+
raise 'should not be raised'
|
816
|
+
end
|
817
|
+
lambda {
|
818
|
+
parse("no revision tag", :git_url => git_url).should == 'no revision tag'
|
819
|
+
}.should_not raise_error
|
820
|
+
end
|
821
|
+
|
822
|
+
describe "absolute_urls" do
|
823
|
+
# 'true' values
|
824
|
+
[true, 1, 'x'].each do |v|
|
825
|
+
it "should NOT convert relative url to absolute when absolute_urls = #{v.inspect} AND site_url is NULL" do
|
826
|
+
parse("[[url:/rel]]", :absolute_urls => v).should ==
|
827
|
+
'<a rel="nofollow" href="/rel">/rel</a>'
|
828
|
+
parse("[[url:/rel|text]]", :absolute_urls => v).should ==
|
829
|
+
'<a rel="nofollow" href="/rel">text</a>'
|
830
|
+
end
|
831
|
+
it "should convert relative url to absolute when absolute_urls = #{v.inspect}" do
|
832
|
+
parse("[[url:/rel]]", :absolute_urls => v, :site_url => 'http://www.ru').should ==
|
833
|
+
'<a rel="nofollow" href="http://www.ru/rel">/rel</a>'
|
834
|
+
parse("[[url:/rel|text]]", :absolute_urls => v, :site_url => 'http://www.ru').should ==
|
835
|
+
'<a rel="nofollow" href="http://www.ru/rel">text</a>'
|
836
|
+
end
|
837
|
+
end
|
838
|
+
# 'false' values
|
839
|
+
[false, nil].each do |v|
|
840
|
+
it "should not convert relative url to absolute when absolute_urls = #{v.inspect}" do
|
841
|
+
parse("[[url:/rel]]", :absolute_urls => v).should ==
|
842
|
+
'<a rel="nofollow" href="/rel">/rel</a>'
|
843
|
+
parse("[[url:/rel|text]]", :absolute_urls => v).should ==
|
844
|
+
'<a rel="nofollow" href="/rel">text</a>'
|
845
|
+
end
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
a = {}
|
850
|
+
a["image:ExistingImage.png"] =
|
851
|
+
'<img src="/spaces/test_space/documents/download/ExistingImage.png" alt="ALT" />'
|
852
|
+
a["image:cVJUz6ejWr35pEab_qKWB8"] =
|
853
|
+
'<img src="/spaces/test_space/documents/download/cVJUz6ejWr35pEab_qKWB8" alt="ALT" />'
|
854
|
+
|
855
|
+
a.each do |k,v|
|
856
|
+
it "parses [[#{k}]]" do
|
857
|
+
parse("[[#{k}]]").should == v.sub('ALT',k.sub('image:',''))
|
858
|
+
end
|
859
|
+
it "parses [[#{k}|привет тест]]" do
|
860
|
+
parse("[[#{k}|привет тест]]").should == v.sub('ALT','привет тест')
|
861
|
+
end
|
862
|
+
it "parses [[#{k}|test & here]]" do
|
863
|
+
parse("[[#{k}|test & here]]").should == v.sub('ALT','test & here')
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
it "ignores unknown link types" do
|
868
|
+
s = "[[zzz:xxx]]"
|
869
|
+
parse(s).should == s
|
870
|
+
s = "[[abcd:1234]]"
|
871
|
+
parse(s).should == s
|
872
|
+
s = "[[abcd::1234]] [[abcd:1234]] [[uri:ww.ru]]"
|
873
|
+
parse(s).should == s
|
874
|
+
end
|
875
|
+
|
876
|
+
it "ignores file & image links with forbidden symbols" do
|
877
|
+
s = "[[file:aaa/bbb]]"
|
878
|
+
parse(s).should == s
|
879
|
+
s = "[[file:aaa\\bbb]]"
|
880
|
+
parse(s).should == s
|
881
|
+
s = "[[file:aaa bbb]]"
|
882
|
+
parse(s).should == s
|
883
|
+
|
884
|
+
s = "[[image:aaa/bbb]]"
|
885
|
+
parse(s).should == s
|
886
|
+
s = "[[image:aaa\\bbb]]"
|
887
|
+
parse(s).should == s
|
888
|
+
s = "[[image:aaa bbb]]"
|
889
|
+
parse(s).should == s
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
###############################################################################
|
894
|
+
###############################################################################
|
895
|
+
###############################################################################
|
896
|
+
|
897
|
+
unless defined?(HTML_ESCAPE)
|
898
|
+
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
|
899
|
+
end
|
900
|
+
|
901
|
+
def h(s)
|
902
|
+
s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
|
903
|
+
end
|
904
|
+
|
905
|
+
def parse(s, h = {})
|
906
|
+
h[:space_name] = "test_space" unless h.key?(:space_name)
|
907
|
+
BreakoutParser.parse_links_only(s,
|
908
|
+
h[:space_name],
|
909
|
+
h[:site_url],
|
910
|
+
h[:git_url],
|
911
|
+
h[:absolute_urls],
|
912
|
+
h[:large_files_url],
|
913
|
+
h[:meta_attributes]
|
914
|
+
).strip
|
915
|
+
end
|
916
|
+
end
|