junebug 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,138 @@
1
+ unless defined? RedCloth
2
+ $:.unshift(File.dirname(__FILE__))
3
+ require 'base'
4
+ end
5
+
6
+ class RedCloth < String
7
+
8
+ DEFAULT_RULES << :markdown
9
+
10
+ # == Markdown
11
+ #
12
+ # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/)
13
+ # block_markdown_setext:: Markdown setext headers
14
+ # block_markdown_atx:: Markdown atx headers
15
+ # block_markdown_rule:: Markdown horizontal rules
16
+ # block_markdown_bq:: Markdown blockquotes
17
+ # block_markdown_lists:: Markdown lists
18
+ # inline_markdown_link:: Markdown links
19
+ #
20
+
21
+ #######
22
+ private
23
+ #######
24
+
25
+ def block_markdown_setext( text )
26
+ if text =~ SETEXT_RE
27
+ tag = if $2 == "="; "h1"; else; "h2"; end
28
+ blk, cont = "<#{ tag }>#{ $1 }</#{ tag }>", $'
29
+ blocks cont
30
+ text.replace( blk + cont )
31
+ end
32
+ end
33
+
34
+ def block_markdown_atx( text )
35
+ if text =~ ATX_RE
36
+ tag = "h#{ $1.length }"
37
+ blk, cont = "<#{ tag }>#{ $2 }</#{ tag }>\n\n", $'
38
+ blocks cont
39
+ text.replace( blk + cont )
40
+ end
41
+ end
42
+
43
+ MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m
44
+
45
+ def block_markdown_bq( text )
46
+ text.gsub!( MARKDOWN_BQ_RE ) do |blk|
47
+ blk.gsub!( /^ *> ?/, '' )
48
+ flush_left blk
49
+ blocks blk
50
+ blk.gsub!( /^(\S)/, "\t\\1" )
51
+ "<blockquote>\n#{ blk }\n</blockquote>\n\n"
52
+ end
53
+ end
54
+
55
+ MARKDOWN_RULE_RE = /^(.*)( ?[#{
56
+ ['*', '-', '_'].collect { |ch| Regexp::quote( ch ) }.join
57
+ }] ?){3,}(.*)$/
58
+
59
+ def block_markdown_rule( text )
60
+ text.gsub!( MARKDOWN_RULE_RE ) do |blk|
61
+ if $3.empty? && $1 =~ /[#{['*', '-', '_'].collect { |ch| Regexp::quote( ch ) }.join}]*/
62
+ "<hr />"
63
+ else
64
+ blk
65
+ end
66
+ end
67
+ end
68
+
69
+ # XXX TODO XXX
70
+ def block_markdown_lists( text )
71
+ end
72
+
73
+ def inline_markdown_link( text )
74
+ end
75
+
76
+ MARKDOWN_REFLINK_RE = /
77
+ \[([^\[\]]+)\] # $text
78
+ [ ]? # opt. space
79
+ (?:\n[ ]*)? # one optional newline followed by spaces
80
+ \[(.*?)\] # $id
81
+ /x
82
+
83
+ def inline_markdown_reflink( text )
84
+ text.gsub!( MARKDOWN_REFLINK_RE ) do |m|
85
+ text, id = $~[1..2]
86
+
87
+ if id.empty?
88
+ url, title = check_refs( text )
89
+ else
90
+ url, title = check_refs( id )
91
+ end
92
+
93
+ atts = " href=\"#{ url }\""
94
+ atts << " title=\"#{ title }\"" if title
95
+ atts = shelve( atts )
96
+
97
+ "<a#{ atts }>#{ text }</a>"
98
+ end
99
+ end
100
+
101
+ MARKDOWN_LINK_RE = /
102
+ \[([^\[\]]+)\] # $text
103
+ \( # open paren
104
+ [ \t]* # opt space
105
+ <?(.+?)>? # $href
106
+ [ \t]* # opt space
107
+ (?: # whole title
108
+ (['"]) # $quote
109
+ (.*?) # $title
110
+ \3 # matching quote
111
+ )? # title is optional
112
+ \)
113
+ /x
114
+
115
+ def inline_markdown_link( text )
116
+ text.gsub!( MARKDOWN_LINK_RE ) do |m|
117
+ text, url, quote, title = $~[1..4]
118
+
119
+ atts = " href=\"#{ url }\""
120
+ atts << " title=\"#{ title }\"" if title
121
+ atts = shelve( atts )
122
+
123
+ "<a#{ atts }>#{ text }</a>"
124
+ end
125
+ end
126
+
127
+ MARKDOWN_REFS_RE = /(^ *)\[([^\n]+?)\]:\s+<?(#{HYPERLINK})>?(?:\s+"((?:[^"]|\\")+)")?(?=\s|$)/m
128
+
129
+ def refs_markdown( text )
130
+ text.gsub!( MARKDOWN_REFS_RE ) do |m|
131
+ flag, url = $~[2..3]
132
+ title = $~[6]
133
+ @urlrefs[flag.downcase] = [url, title]
134
+ nil
135
+ end
136
+ end
137
+ end
138
+
@@ -0,0 +1,449 @@
1
+ unless defined? RedCloth
2
+ $:.unshift(File.dirname(__FILE__))
3
+ require 'base'
4
+ end
5
+
6
+ class RedCloth < String
7
+
8
+ DEFAULT_RULES << :textile
9
+
10
+ #
11
+ # == Textile Rules
12
+ #
13
+ # The following textile rules can be set individually. Or add the complete
14
+ # set of rules with the single :textile rule, which supplies the rule set in
15
+ # the following precedence:
16
+ #
17
+ # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/)
18
+ # block_textile_table:: Textile table block structures
19
+ # block_textile_lists:: Textile list structures
20
+ # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.)
21
+ # block_textile_defs:: Textile definition lists
22
+ # inline_textile_image:: Textile inline images
23
+ # inline_textile_link:: Textile inline links
24
+ # inline_textile_span:: Textile inline spans
25
+ # glyphs_textile:: Textile entities (such as em-dashes and smart quotes)
26
+ #
27
+
28
+ #######
29
+ private
30
+ #######
31
+ #
32
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
33
+ # (from PyTextile)
34
+ #
35
+ TEXTILE_TAGS =
36
+
37
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
38
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
39
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
40
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
41
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
42
+
43
+ collect! do |a, b|
44
+ [a.chr, ( b.zero? and "" or "&##{ b };" )]
45
+ end
46
+
47
+ def textile_pre_process(text)
48
+ {'w' => 'warning', 'n' => 'note', 'c' => 'comment', 'pro' => 'production', 'dt' => 'dt', 'dd' => 'dd'}.each do |char, word|
49
+ parts = text.split(/^\s*#{char}\./)
50
+ text.replace(parts.first + "\n" + parts[1..-1].map do |part|
51
+ if part =~ /\.#{char}\s*$/
52
+ "div(#{word})." + part.sub(/\.#{char}\s*$/, "div(#{word}). \n")
53
+ else
54
+ "#{char}.#{part}"
55
+ end
56
+ end.join("\n"))
57
+
58
+ self.class.class_eval %!
59
+ def textile_#{char}(tag, atts, cite, content)
60
+ textile_p('p', %{ class=#{word.inspect}}, cite, content)
61
+ end
62
+ !
63
+ end
64
+ {'bq' => 'blockquote'}.each do |char, word|
65
+ parts = text.split(/^\s*#{char}\./)
66
+ text.replace(parts.first + "\n" + parts[1..-1].map do |part|
67
+ if part =~ /\.#{char}\s*$/
68
+ "div(#{word})." + part.sub(/\.#{char}\s*$/, "div(#{word}). ")
69
+ else
70
+ "#{char}.#{part}"
71
+ end
72
+ end.join("\n"))
73
+ end
74
+
75
+ text.gsub!( BACKTICK_CODE_RE ) do |m|
76
+ before,lang,code,after = $~[1..4]
77
+ lang = " lang=\"#{ lang }\"" if lang
78
+ rip_offtags( "#{ before }<pre><code#{ lang }>#{ code.gsub(/\\\`\`\`/,'```') }</code></pre>#{ after }" )
79
+ end
80
+ end
81
+
82
+ def textile_post_process(text)
83
+ post_inline_textile_span(text)
84
+ end
85
+
86
+ # Parses a Textile table block, building HTML from the result.
87
+ def block_textile_table( text )
88
+ text.gsub!( TABLE_RE ) do |matches|
89
+
90
+ caption, id, tatts, fullrow = $~[1..4]
91
+ tatts = pba( tatts, 'table' )
92
+ tatts = shelve( tatts ) if tatts
93
+ rows = []
94
+
95
+ fullrow.
96
+ split( /\|$/m ).
97
+ delete_if {|row|row.empty?}.
98
+ each do |row|
99
+
100
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
101
+ row << " "
102
+
103
+ cells = []
104
+ row.split( '|' ).each_with_index do |cell, i|
105
+ next if i == 0
106
+
107
+ ctyp = 'd'
108
+ ctyp = 'h' if cell =~ /^_/
109
+
110
+ catts = ''
111
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/
112
+
113
+ catts = shelve( catts ) if catts
114
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell.strip.empty? ? "&nbsp;" : row.split( '|' ).size-1 != i ? cell : cell[0...cell.length-1] }</t#{ ctyp }>"
115
+ end
116
+ ratts = shelve( ratts ) if ratts
117
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
118
+ end
119
+ caption = "\t<p class=\"caption\">#{caption}</p>\n" if caption
120
+ "#{caption}\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
121
+ end
122
+ end
123
+
124
+ # Parses Textile lists and generates HTML
125
+ def block_textile_lists( text )
126
+ orig_text = text.dup
127
+
128
+ # Take care of _*'s and _#'s to turn them into paragraphs
129
+ text.gsub!(/([\*#] )((.*?\n\s*_[\*#].*?)+)/) do |m|
130
+ "#{$1}<p>"+$2.split(/_[\*#]/).map{|w|w.strip}.delete_if{|w|w.empty?}.join("</p><p>")+"</p>"
131
+ end
132
+
133
+ @last_line ||= -1
134
+
135
+ text.gsub!( LISTS_RE ) do |match|
136
+ if text =~ /^#([_0-9]+).*/m
137
+ if $1 == $1.to_i.to_s # then it is a number, so use it
138
+ @last_line = $1.to_i - 2
139
+ end
140
+ else
141
+ @last_line = -1
142
+ end
143
+ lines = match.split( /\n/ )
144
+ depth = []
145
+ lines.each_with_index do |line, line_id|
146
+ if line =~ LISTS_CONTENT_RE
147
+
148
+ tl,continuation,atts,content = $~[1..4]
149
+ @last_line += 1 if tl.length == 1
150
+
151
+ unless depth.last.nil?
152
+ if depth.last.length > tl.length
153
+ (depth.length - 1).downto(0) do |i|
154
+ break if depth[i].length == tl.length
155
+ lines[line_id - 1] << "</li>\n#{"\t"*(depth.size-1)}</#{ lT( depth[i] ) }l>"
156
+ depth.pop
157
+ tab_in = true
158
+ end
159
+ end
160
+ if depth.last && depth.last.length == tl.length
161
+ lines[line_id - 1] << "</li>"
162
+ end
163
+ end
164
+ unless depth.last == tl
165
+ depth << tl
166
+ atts = pba( atts )
167
+ atts << " start=\"#{@last_line + 1}\"" if lT(tl) == "o" && !continuation.empty? && @last_line > 0
168
+ atts = shelve( atts ) if atts
169
+ lines[line_id] = "#{"\t"*(depth.size-1)}<#{ lT(tl) }l#{ atts }>\n#{"\t"*depth.size}<li>#{ content }"
170
+ else
171
+ lines[line_id] = "#{"\t"*depth.size}<li>#{ content }"
172
+ end
173
+ elsif line =~ /^([_]+)(#{A}#{C}) (.*)$/m
174
+ @last_line += 1
175
+ tl = "u"
176
+ atts,content = $~[2..3]
177
+
178
+ unless depth.last.nil?
179
+ if depth.last.length > tl.length
180
+ (depth.length - 1).downto(0) do |i|
181
+ break if depth[i].length == tl.length
182
+ lines[line_id - 1] << "</li>\n#{"\t"*(depth.size-1)}</#{ lT( depth[i] ) }l>"
183
+ depth.pop
184
+ tab_in = true
185
+ end
186
+ end
187
+ if depth.last and depth.last.length == tl.length
188
+ lines[line_id - 1] << "</li>"
189
+ end
190
+ end
191
+ unless depth.last == tl
192
+ depth << tl
193
+ atts = pba( atts )
194
+ atts = shelve( "#{atts} style=\"list-style-type:none;\"" )
195
+ lines[line_id] = "#{"\t"*(depth.size-1)}<#{ lT(tl) }l#{ atts }>\n#{"\t"*depth.size}<li>#{ content }"
196
+ else
197
+ lines[line_id] = "#{"\t"*depth.size}<li>#{ content }"
198
+ end
199
+ end
200
+
201
+ if line_id == lines.length - 1
202
+ tabs = depth.size-1
203
+ depth.reverse.delete_if do |v|
204
+ lines[-1] << "</li>\n#{"\t"*tabs}</#{ lT( v ) }l>"
205
+ tabs -= 1
206
+ end
207
+ end
208
+ end
209
+ lines.join( "\n" )
210
+ end
211
+
212
+ text != orig_text
213
+ end
214
+
215
+ # Parses Textile definition lists and generates HTML
216
+ def block_textile_defs( text )
217
+ text.gsub!(/^-\s+(.*?):=(.*?)=:\s*$/m) do |m|
218
+ "- #{$1.strip} := <p>"+$2.split(/\n/).map{|w|w.strip}.delete_if{|w|w.empty?}.join("</p><p>")+"</p>"
219
+ end
220
+
221
+ text.gsub!( DEFS_RE ) do |match|
222
+ lines = match.split( /\n/ )
223
+ lines.each_with_index do |line, line_id|
224
+ if line =~ DEFS_CONTENT_RE
225
+ dl,continuation,dt,dd = $~[1..4]
226
+
227
+ atts = pba( atts )
228
+ atts = shelve( atts ) if atts
229
+ lines[line_id] = line_id == 0 ? "<dl#{ atts }>" : ""
230
+ lines[line_id] << "\n\t<dt>#{ dt.strip }</dt>\n\t<dd>#{ dd.strip }</dd>"
231
+ end
232
+
233
+ if line_id == lines.length - 1
234
+ lines[-1] << "\n</dl>"
235
+ end
236
+ end
237
+ lines.join( "\n" )
238
+ end
239
+ end
240
+
241
+ def inline_textile_code( text )
242
+ text.gsub!( CODE_RE ) do |m|
243
+ before,lang,code,after = $~[1..4]
244
+ lang = " lang=\"#{ lang }\"" if lang
245
+ rip_offtags( "#{ before }<code#{ lang }>#{ code.gsub(/\\@(@?)/,'@\1') }</code>#{ after }" )
246
+ end
247
+ end
248
+
249
+ def textile_bq( tag, atts, cite, content )
250
+ cite, cite_title = check_refs( cite )
251
+ cite = " cite=\"#{ cite }\"" if cite
252
+ atts = shelve( atts ) if atts
253
+ "\t<blockquote#{ cite }>\n\t\t<p#{ atts }>#{ content }</p>\n\t</blockquote>"
254
+ end
255
+
256
+ def textile_p( tag, atts, cite, content )
257
+ atts = shelve( atts ) if atts
258
+ "\t<#{ tag }#{ atts }>#{ content }</#{ tag }>"
259
+ end
260
+
261
+ alias textile_h1 textile_p
262
+ alias textile_h2 textile_p
263
+ alias textile_h3 textile_p
264
+ alias textile_h4 textile_p
265
+ alias textile_h5 textile_p
266
+ alias textile_h6 textile_p
267
+
268
+ def textile_fn_( tag, num, atts, cite, content )
269
+ atts << " id=\"fn#{ num }\""
270
+ content = "<sup>#{ num }</sup> #{ content }"
271
+ atts = shelve( atts ) if atts
272
+ "\t<p#{ atts }>#{ content }</p>"
273
+ end
274
+
275
+ def textile_ch( tag, atts, cite, content )
276
+ textile_p("h1", atts, cite, content)
277
+ end
278
+
279
+ def block_textile_prefix( text )
280
+ if text =~ BLOCK_RE
281
+ tag,tagpre,num,atts,cite,content = $~[1..6]
282
+ atts = pba( atts )
283
+
284
+ # pass to prefix handler
285
+ if respond_to? "textile_#{ tag }", true
286
+ text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) )
287
+ elsif respond_to? "textile_#{ tagpre }_", true
288
+ text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) )
289
+ end
290
+ end
291
+ end
292
+
293
+ def inline_textile_span( text )
294
+ QTAGS.each do |qtag_rc, ht, qtag_re, rtype, escaped_re|
295
+ text.gsub!( qtag_re ) do |m|
296
+
297
+ case rtype
298
+ when :limit
299
+ sta,qtag,atts,cite,content = $~[1..5]
300
+ else
301
+ qtag,atts,cite,content = $~[1..4]
302
+ sta = ''
303
+ end
304
+ atts = pba( atts )
305
+ atts << " cite=\"#{ cite }\"" if cite
306
+ atts = shelve( atts ) if atts
307
+
308
+ "#{ sta }<#{ ht }#{ atts }>#{ content }</#{ ht }>"
309
+ end
310
+ end
311
+ end
312
+
313
+ def post_inline_textile_span( text )
314
+ QTAGS.each do |qtag_rc, ht, qtag_re, rtype, escaped_re|
315
+ text.gsub!( escaped_re ) do |m|
316
+
317
+ case rtype
318
+ when :limit
319
+ sta,qtag,atts,cite,content = $~[1..5]
320
+ else
321
+ qtag,atts,cite,content = $~[1..4]
322
+ sta = ''
323
+ end
324
+ atts = pba( atts )
325
+ atts << " cite=\"#{ cite }\"" if cite
326
+
327
+ "#{ sta }<#{ ht }#{ atts }>#{ content }</#{ ht }>"
328
+ end
329
+ end
330
+ end
331
+
332
+ def inline_textile_link( text )
333
+ text.gsub!( LINK_RE ) do |m|
334
+ pre,atts,text,title,url,slash,post = $~[1..7]
335
+
336
+ url, url_title = check_refs( url )
337
+ title ||= url_title
338
+
339
+ atts = pba( atts )
340
+ atts = " href=\"#{ url }#{ slash }\"#{ atts }"
341
+ atts << " title=\"#{ title }\"" if title
342
+ atts = shelve( atts ) if atts
343
+
344
+ "#{ pre }<a#{ atts }>#{ text }</a>#{ post }"
345
+ end
346
+ end
347
+
348
+ TEXTILE_REFS_RE = /(^ *)\[([^\n\[]+?)\](#{HYPERLINK})(?=\s|$)/
349
+
350
+ def refs_textile( text )
351
+ text.gsub!( TEXTILE_REFS_RE ) do |m|
352
+ flag, url = $~[2..3]
353
+ @urlrefs[flag.downcase] = [url, nil]
354
+ nil
355
+ end
356
+ end
357
+
358
+ def inline_textile_image( text )
359
+ text.gsub!( IMAGE_RE ) do |m|
360
+ stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
361
+ atts = pba( atts )
362
+ atts = " src=\"#{ url }\"#{ atts }"
363
+ atts << " title=\"#{ title }\"" if title
364
+ atts << " alt=\"#{ title }\""
365
+ # size = @getimagesize($url);
366
+ # if($size) $atts.= " $size[3]";
367
+
368
+ href, alt_title = check_refs( href ) if href
369
+ url, url_title = check_refs( url )
370
+
371
+ out = ''
372
+ out << "<a#{ shelve( " href=\"#{ href }\"" ) }>" if href
373
+ out << "<img#{ shelve( atts ) } />"
374
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
375
+
376
+ if algn
377
+ algn = h_align( algn )
378
+ if stln == "<p>"
379
+ out = "<p style=\"float:#{ algn }\">#{ out }"
380
+ else
381
+ out = "#{ stln }<div style=\"float:#{ algn }\">#{ out }</div>"
382
+ end
383
+ else
384
+ out = stln + out
385
+ end
386
+
387
+ out
388
+ end
389
+ end
390
+
391
+ def no_textile( text )
392
+ text.gsub!( /(^|\s)(\\?)==([^=]+.*?)\2==(\s|$)?/ ) do |m|
393
+ $2.empty? ? "#{$1}<notextile>#{$3}</notextile>#{$4}" : "#{$1}==#{$3}==#{$4}"
394
+ end
395
+ text.gsub!( /^ *(\\?)==([^=]+.*?)\1==/m ) do |m|
396
+ $1.empty? ? "<notextile>#{$2}</notextile>" : "==#{$2}=="
397
+ end
398
+ end
399
+
400
+ def glyphs_textile( text, level = 0 )
401
+ if text !~ HASTAG_MATCH
402
+ pgl text
403
+ footnote_ref text
404
+ else
405
+ codepre = 0
406
+ text.gsub!( ALLTAG_MATCH ) do |line|
407
+ ## matches are off if we're between <code>, <pre> etc.
408
+ if $1
409
+ if line =~ OFFTAG_OPEN
410
+ codepre += 1
411
+ elsif line =~ OFFTAG_CLOSE
412
+ codepre -= 1
413
+ codepre = 0 if codepre < 0
414
+ end
415
+ elsif codepre.zero?
416
+ glyphs_textile( line, level + 1 )
417
+ else
418
+ htmlesc( line, :NoQuotes )
419
+ end
420
+ ## p [level, codepre, orig_line, line]
421
+
422
+ line
423
+ end
424
+ end
425
+ end
426
+
427
+ def textile_popup_help( name, windowW, windowH )
428
+ ' <a target="_blank" href="http://hobix.com/textile/#' + helpvar + '" onclick="window.open(this.href, \'popupwindow\', \'width=' + windowW + ',height=' + windowH + ',scrollbars,resizable\'); return false;">' + name + '</a><br />'
429
+ end
430
+
431
+ # Turns all urls into clickable links.
432
+ # Taken from ActionPack's ActionView
433
+ def inline_textile_autolink_urls(text)
434
+ text.gsub!(AUTO_LINK_RE) do
435
+ all, a, b, c, d = $&, $1, $2, $3, $5
436
+ if a =~ /<a\s/i # don't replace URL's that are already linked
437
+ all
438
+ else
439
+ %(#{a}<a href="#{b=="www."?"http://www.":b}#{c}">#{b}#{c}</a>#{d})
440
+ end
441
+ end
442
+ end
443
+
444
+ # Turns all email addresses into clickable links.
445
+ def inline_textile_autolink_emails(text)
446
+ text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/, '<a href="mailto:\1">\1</a>')
447
+ end
448
+ end
449
+